]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
Add support for horizontal mouse wheel scrolling in wxSTC.
[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
7fe8059f 7// RCS-ID: $Id$
5d7836c4
JS
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
2be72ac2 11
5d7836c4
JS
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
61399247 16 #pragma hdrstop
5d7836c4
JS
17#endif
18
b01ca8b6
JS
19#if wxUSE_RICHTEXT
20
21#include "wx/richtext/richtextbuffer.h"
22
5d7836c4 23#ifndef WX_PRECOMP
61399247
WS
24 #include "wx/dc.h"
25 #include "wx/intl.h"
7947a48a 26 #include "wx/log.h"
28f92d74 27 #include "wx/dataobj.h"
02761f6c 28 #include "wx/module.h"
5d7836c4
JS
29#endif
30
0ec6da02 31#include "wx/settings.h"
5d7836c4
JS
32#include "wx/filename.h"
33#include "wx/clipbrd.h"
34#include "wx/wfstream.h"
5d7836c4
JS
35#include "wx/mstream.h"
36#include "wx/sstream.h"
0ca07313 37#include "wx/textfile.h"
44cc96a8 38#include "wx/hashmap.h"
cdaed652 39#include "wx/dynarray.h"
5d7836c4 40
5d7836c4
JS
41#include "wx/richtext/richtextctrl.h"
42#include "wx/richtext/richtextstyles.h"
cdaed652 43#include "wx/richtext/richtextimagedlg.h"
603f702b 44#include "wx/richtext/richtextsizepage.h"
1aca9fcd 45#include "wx/richtext/richtextxml.h"
5d7836c4
JS
46
47#include "wx/listimpl.cpp"
bec80f4f 48#include "wx/arrimpl.cpp"
5d7836c4 49
412e0d47
DS
50WX_DEFINE_LIST(wxRichTextObjectList)
51WX_DEFINE_LIST(wxRichTextLineList)
5d7836c4 52
ea160b2e
JS
53// Switch off if the platform doesn't like it for some reason
54#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55
31778480
JS
56// Use GetPartialTextExtents for platforms that support it natively
57#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58
ff76711f
JS
59const wxChar wxRichTextLineBreakChar = (wxChar) 29;
60
cdaed652
VZ
61// Helper classes for floating layout
62struct wxRichTextFloatRectMap
63{
64 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
65 {
66 startY = sY;
67 endY = eY;
68 width = w;
69 anchor = obj;
70 }
71
72 int startY, endY;
73 int width;
74 wxRichTextObject* anchor;
75};
76
77WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
78
79int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
80{
81 return r1->startY - r2->startY;
82}
83
84class wxRichTextFloatCollector
85{
86public:
603f702b 87 wxRichTextFloatCollector(const wxRect& availableRect);
cdaed652
VZ
88 ~wxRichTextFloatCollector();
89
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph* para);
92 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
93
94 // Return the last paragraph we collected
95 wxRichTextParagraph* LastParagraph();
96
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect GetAvailableRect(int startY, int endY);
100
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction, int start, int height) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
104
105 // Find the last y position
106 int GetLastRectBottom();
107
108 // Draw the floats inside a rect
8db2e3ef 109 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652
VZ
110
111 // HitTest the floats
8db2e3ef 112 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
603f702b
JS
113
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
116
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
cdaed652 119
07d4142f
JS
120 // Delete a float
121 bool DeleteFloat(wxRichTextObject* obj);
122
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject* obj);
125
126 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
127
cdaed652
VZ
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
129
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
ce00f59b 131
cdaed652
VZ
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
133
8db2e3ef 134 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652 135
8db2e3ef 136 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
ce00f59b 137
cdaed652
VZ
138private:
139 wxRichTextFloatRectMapArray m_left;
140 wxRichTextFloatRectMapArray m_right;
603f702b
JS
141 //int m_width;
142 wxRect m_availableRect;
cdaed652
VZ
143 wxRichTextParagraph* m_para;
144};
145
07d4142f
JS
146// Delete a float
147bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
148{
149 size_t i;
150 for (i = 0; i < m_left.GetCount(); i++)
151 {
152 if (m_left[i]->anchor == obj)
153 {
154 m_left.RemoveAt(i);
155 return true;
156 }
157 }
158 for (i = 0; i < m_right.GetCount(); i++)
159 {
160 if (m_right[i]->anchor == obj)
161 {
162 m_right.RemoveAt(i);
163 return true;
164 }
165 }
166 return false;
167}
168
169// Do we have this float already?
170bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
171{
172 size_t i;
173 for (i = 0; i < m_left.GetCount(); i++)
174 {
175 if (m_left[i]->anchor == obj)
176 {
177 return true;
178 }
179 }
180 for (i = 0; i < m_right.GetCount(); i++)
181 {
182 if (m_right[i]->anchor == obj)
183 {
184 return true;
185 }
186 }
187 return false;
188}
189
603f702b
JS
190// Get floating objects
191bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
192{
193 size_t i;
194 for (i = 0; i < m_left.GetCount(); i++)
195 objects.Append(m_left[i]->anchor);
196 for (i = 0; i < m_right.GetCount(); i++)
197 objects.Append(m_right[i]->anchor);
198 return true;
199}
200
201
cdaed652
VZ
202/*
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
207 */
208int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
209{
210 int end = array.GetCount() - 1;
211 int start = 0;
212 int ret = 0;
213
214 wxASSERT(end >= 0);
215
216 while (true)
217 {
218 if (start > end)
219 {
220 break;
221 }
222
223 int mid = (start + end) / 2;
224 if (array[mid]->startY <= point && array[mid]->endY >= point)
225 return mid;
226 else if (array[mid]->startY > point)
227 {
228 end = mid - 1;
229 ret = mid;
230 }
231 else if (array[mid]->endY < point)
232 {
233 start = mid + 1;
234 ret = start;
235 }
236 }
237
238 return ret;
239}
240
241int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
242{
243 int ret = 0;
244 int len = array.GetCount();
245
246 wxASSERT(index >= 0 && index < len);
247
248 if (array[index]->startY < startY && array[index]->endY > startY)
249 ret = ret < array[index]->width ? array[index]->width : ret;
250 while (index < len && array[index]->startY <= endY)
251 {
252 ret = ret < array[index]->width ? array[index]->width : ret;
253 index++;
254 }
255
256 return ret;
257}
258
603f702b 259wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
cdaed652 260{
603f702b 261 m_availableRect = rect;
cdaed652
VZ
262 m_para = NULL;
263}
264
265void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
266{
267 int len = array.GetCount();
268 for (int i = 0; i < len; i++)
269 delete array[i];
270}
271
272wxRichTextFloatCollector::~wxRichTextFloatCollector()
273{
274 FreeFloatRectMapArray(m_left);
275 FreeFloatRectMapArray(m_right);
276}
277
278int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
279{
280 if (array.GetCount() == 0)
281 return start;
282
603f702b 283 int i = SearchAdjacentRect(array, start);
cdaed652 284 int last = start;
603f702b 285 while (i < (int) array.GetCount())
cdaed652
VZ
286 {
287 if (array[i]->startY - last >= height)
288 return last + 1;
289 last = array[i]->endY;
290 i++;
291 }
292
293 return last + 1;
294}
295
296int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
297{
24777478 298 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
cdaed652 299 return GetFitPosition(m_left, start, height);
24777478 300 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
cdaed652
VZ
301 return GetFitPosition(m_right, start, height);
302 else
303 {
304 wxASSERT("Never should be here");
305 return start;
306 }
307}
308
603f702b
JS
309// Adds a floating image to the float collector.
310// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
cdaed652
VZ
311void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
312{
603f702b 313 int direction = floating->GetFloatDirection();
24777478 314
603f702b
JS
315 wxPoint pos = floating->GetPosition();
316 wxSize size = floating->GetCachedSize();
317 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
318 switch (direction)
319 {
320 case wxTEXT_BOX_ATTR_FLOAT_NONE:
321 delete map;
322 break;
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
326 m_left.Add(map);
327 break;
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
330 m_right.Add(map);
331 break;
332 default:
333 delete map;
334 wxASSERT("Unrecognised float attribute.");
335 }
cdaed652 336
603f702b 337 m_para = para;
cdaed652
VZ
338}
339
340void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
341{
342 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
343 while (node)
344 {
345 wxRichTextObject* floating = node->GetData();
ce00f59b 346
cdaed652
VZ
347 if (floating->IsFloating())
348 {
bec80f4f 349 CollectFloat(para, floating);
cdaed652 350 }
ce00f59b 351
cdaed652
VZ
352 node = node->GetNext();
353 }
354
355 m_para = para;
356}
357
358wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
359{
360 return m_para;
361}
362
363wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
364{
365 int widthLeft = 0, widthRight = 0;
366 if (m_left.GetCount() != 0)
367 {
603f702b
JS
368 int i = SearchAdjacentRect(m_left, startY);
369 if (i < (int) m_left.GetCount())
cdaed652
VZ
370 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
371 }
372 if (m_right.GetCount() != 0)
373 {
603f702b
JS
374 int j = SearchAdjacentRect(m_right, startY);
375 if (j < (int) m_right.GetCount())
cdaed652
VZ
376 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
377 }
378
603f702b
JS
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
cdaed652
VZ
383}
384
385int wxRichTextFloatCollector::GetLastRectBottom()
386{
387 int ret = 0;
388 int len = m_left.GetCount();
389 if (len) {
390 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
391 }
392 len = m_right.GetCount();
393 if (len) {
394 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
395 }
396
397 return ret;
398}
399
8db2e3ef 400void 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
401{
402 int start = rect.y;
403 int end = rect.y + rect.height;
603f702b 404 int i, j;
cdaed652 405 i = SearchAdjacentRect(array, start);
603f702b 406 if (i < 0 || i >= (int) array.GetCount())
cdaed652
VZ
407 return;
408 j = SearchAdjacentRect(array, end);
603f702b 409 if (j < 0 || j >= (int) array.GetCount())
cdaed652
VZ
410 j = array.GetCount() - 1;
411 while (i <= j)
412 {
413 wxRichTextObject* obj = array[i]->anchor;
414 wxRichTextRange r = obj->GetRange();
8db2e3ef 415 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
cdaed652
VZ
416 i++;
417 }
418}
ecb5fbf1 419
8db2e3ef 420void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
421{
422 if (m_left.GetCount() > 0)
8db2e3ef 423 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
cdaed652 424 if (m_right.GetCount() > 0)
8db2e3ef 425 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
cdaed652
VZ
426}
427
8db2e3ef 428int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
cdaed652 429{
603f702b 430 int i;
cdaed652
VZ
431 if (array.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE;
433 i = SearchAdjacentRect(array, pt.y);
603f702b 434 if (i < 0 || i >= (int) array.GetCount())
cdaed652 435 return wxRICHTEXT_HITTEST_NONE;
603f702b
JS
436 if (!array[i]->anchor->IsShown())
437 return wxRICHTEXT_HITTEST_NONE;
438
cdaed652
VZ
439 wxPoint point = array[i]->anchor->GetPosition();
440 wxSize size = array[i]->anchor->GetCachedSize();
441 if (point.x <= pt.x && point.x + size.x >= pt.x
442 && point.y <= pt.y && point.y + size.y >= pt.y)
443 {
444 textPosition = array[i]->anchor->GetRange().GetStart();
603f702b 445 * obj = array[i]->anchor;
cdaed652
VZ
446 if (pt.x > (pt.x + pt.x + size.x) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE;
448 else
449 return wxRICHTEXT_HITTEST_AFTER;
450 }
ce00f59b 451
cdaed652
VZ
452 return wxRICHTEXT_HITTEST_NONE;
453}
454
8db2e3ef 455int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
cdaed652 456{
8db2e3ef 457 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
458 if (ret == wxRICHTEXT_HITTEST_NONE)
459 {
8db2e3ef 460 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
461 }
462 return ret;
463}
464
ce00f59b 465// Helpers for efficiency
ecb5fbf1
JS
466inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
467{
ecb5fbf1
JS
468 dc.SetFont(font);
469}
470
471inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
472{
473 const wxPen& pen1 = dc.GetPen();
474 if (pen1.IsOk() && pen.IsOk())
475 {
476 if (pen1.GetWidth() == pen.GetWidth() &&
477 pen1.GetStyle() == pen.GetStyle() &&
478 pen1.GetColour() == pen.GetColour())
479 return;
480 }
481 dc.SetPen(pen);
482}
483
484inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
485{
486 const wxBrush& brush1 = dc.GetBrush();
487 if (brush1.IsOk() && brush.IsOk())
488 {
489 if (brush1.GetStyle() == brush.GetStyle() &&
490 brush1.GetColour() == brush.GetColour())
491 return;
492 }
493 dc.SetBrush(brush);
494}
495
5d7836c4
JS
496/*!
497 * wxRichTextObject
498 * This is the base for drawable objects.
499 */
500
501IMPLEMENT_CLASS(wxRichTextObject, wxObject)
502
503wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
504{
5d7836c4
JS
505 m_refCount = 1;
506 m_parent = parent;
5d7836c4 507 m_descent = 0;
603f702b 508 m_show = true;
5d7836c4
JS
509}
510
511wxRichTextObject::~wxRichTextObject()
512{
513}
514
515void wxRichTextObject::Dereference()
516{
517 m_refCount --;
518 if (m_refCount <= 0)
519 delete this;
520}
521
522/// Copy
523void wxRichTextObject::Copy(const wxRichTextObject& obj)
524{
525 m_size = obj.m_size;
603f702b
JS
526 m_maxSize = obj.m_maxSize;
527 m_minSize = obj.m_minSize;
5d7836c4 528 m_pos = obj.m_pos;
5d7836c4 529 m_range = obj.m_range;
603f702b 530 m_ownRange = obj.m_ownRange;
5d7836c4 531 m_attributes = obj.m_attributes;
bec80f4f 532 m_properties = obj.m_properties;
5d7836c4 533 m_descent = obj.m_descent;
603f702b
JS
534 m_show = obj.m_show;
535}
536
537// Get/set the top-level container of this object.
538wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
539{
540 const wxRichTextObject* p = this;
541 while (p)
542 {
543 if (p->IsTopLevel())
544 {
545 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
546 }
547 p = p->GetParent();
548 }
549 return NULL;
5d7836c4
JS
550}
551
552void wxRichTextObject::SetMargins(int margin)
553{
603f702b 554 SetMargins(margin, margin, margin, margin);
5d7836c4
JS
555}
556
557void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
558{
603f702b
JS
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
563}
564
565int wxRichTextObject::GetLeftMargin() const
566{
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
568}
569
570int wxRichTextObject::GetRightMargin() const
571{
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
573}
574
575int wxRichTextObject::GetTopMargin() const
576{
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
578}
579
580int wxRichTextObject::GetBottomMargin() const
581{
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
583}
584
585// Calculate the available content space in the given rectangle, given the
586// margins, border and padding specified in the object's attributes.
8db2e3ef 587wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
603f702b
JS
588{
589 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590 marginRect = outerRect;
8db2e3ef
JS
591 wxRichTextAttr attr(GetAttributes());
592 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
594 return contentRect;
595}
596
597// Invalidate the buffer. With no argument, invalidates whole buffer.
598void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
599{
600 if (invalidRange != wxRICHTEXT_NONE)
601 {
23698b12
JS
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
e12b91a3 605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
23698b12 606 SetCachedSize(wxDefaultSize);
603f702b
JS
607 SetMaxSize(wxDefaultSize);
608 SetMinSize(wxDefaultSize);
609 }
5d7836c4
JS
610}
611
44219ff0 612// Convert units in tenths of a millimetre to device units
cdaed652 613int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
5d7836c4 614{
44219ff0 615 // Unscale
bec80f4f
JS
616 double scale = 1.0;
617 if (GetBuffer())
32423dd8 618 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
bec80f4f
JS
619 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
620
44219ff0
JS
621 return p;
622}
623
624// Convert units in tenths of a millimetre to device units
bec80f4f 625int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
44219ff0 626{
5d7836c4
JS
627 // There are ppi pixels in 254.1 "1/10 mm"
628
629 double pixels = ((double) units * (double)ppi) / 254.1;
bec80f4f
JS
630 if (scale != 1.0)
631 pixels /= scale;
5d7836c4 632
603f702b
JS
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels == 0 && units > 0)
635 pixels = 1;
636
5d7836c4
JS
637 return (int) pixels;
638}
639
24777478
JS
640// Convert units in pixels to tenths of a millimetre
641int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
642{
643 int p = pixels;
bec80f4f
JS
644 double scale = 1.0;
645 if (GetBuffer())
646 scale = GetBuffer()->GetScale();
647
648 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
24777478
JS
649}
650
bec80f4f 651int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
24777478
JS
652{
653 // There are ppi pixels in 254.1 "1/10 mm"
bec80f4f
JS
654
655 double p = double(pixels);
656
657 if (scale != 1.0)
658 p *= scale;
659
660 int units = int( p * 254.1 / (double) ppi );
24777478
JS
661 return units;
662}
663
bec80f4f 664// Draw the borders and background for the given rectangle and attributes.
603f702b
JS
665// Width and height are taken to be the outer margin size, not the content.
666bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
bec80f4f
JS
667{
668 // Assume boxRect is the area around the content
603f702b
JS
669 wxRect marginRect = boxRect;
670 wxRect contentRect, borderRect, paddingRect, outlineRect;
bec80f4f 671
603f702b 672 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
bec80f4f
JS
673
674 // Margin is transparent. Draw background from margin.
603f702b 675 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
bec80f4f 676 {
603f702b
JS
677 wxColour colour;
678 if (flags & wxRICHTEXT_DRAW_SELECTED)
679 {
680 // TODO: get selection colour from control?
681 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
682 }
683 else
684 colour = attr.GetBackgroundColour();
685
686 wxPen pen(colour);
687 wxBrush brush(colour);
bec80f4f
JS
688
689 dc.SetPen(pen);
690 dc.SetBrush(brush);
37e7b783 691 dc.DrawRectangle(borderRect);
bec80f4f
JS
692 }
693
603f702b
JS
694 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
695 {
696 wxRichTextAttr editBorderAttr = attr;
697 // TODO: make guideline colour configurable
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
701
702 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
703 }
704
705 if (attr.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
bec80f4f 707
603f702b
JS
708 if (attr.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
bec80f4f
JS
710
711 return true;
712}
713
714// Draw a border
603f702b 715bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
bec80f4f
JS
716{
717 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
603f702b 718 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 719
603f702b 720 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
721 {
722 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723 wxColour col(attr.GetLeft().GetColour());
724
725 // If pen width is > 1, resorts to a solid rectangle.
726 if (borderLeft == 1)
727 {
728 int penStyle = wxSOLID;
729 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
730 penStyle = wxDOT;
731 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732 penStyle = wxLONG_DASH;
603f702b 733 wxPen pen(col, 1, penStyle);
bec80f4f
JS
734 dc.SetPen(pen);
735 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
736
737 }
738 else if (borderLeft > 1)
739 {
740 wxPen pen(col);
741 wxBrush brush(col);
742 dc.SetPen(pen);
743 dc.SetBrush(brush);
744 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
745 }
746 }
747
603f702b 748 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
749 {
750 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
751
752 wxColour col(attr.GetRight().GetColour());
753
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight == 1)
756 {
757 int penStyle = wxSOLID;
758 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
759 penStyle = wxDOT;
760 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761 penStyle = wxLONG_DASH;
603f702b 762 wxPen pen(col, 1, penStyle);
bec80f4f 763 dc.SetPen(pen);
603f702b 764 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
bec80f4f
JS
765
766 }
767 else if (borderRight > 1)
768 {
769 wxPen pen(col);
770 wxBrush brush(col);
771 dc.SetPen(pen);
772 dc.SetBrush(brush);
63af79de 773 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
bec80f4f
JS
774 }
775 }
776
603f702b 777 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
778 {
779 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
780
781 wxColour col(attr.GetTop().GetColour());
782
783 // If pen width is > 1, resorts to a solid rectangle.
784 if (borderTop == 1)
785 {
786 int penStyle = wxSOLID;
787 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
788 penStyle = wxDOT;
789 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790 penStyle = wxLONG_DASH;
603f702b 791 wxPen pen(col, 1, penStyle);
bec80f4f
JS
792 dc.SetPen(pen);
793 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
794
795 }
796 else if (borderTop > 1)
797 {
798 wxPen pen(col);
799 wxBrush brush(col);
800 dc.SetPen(pen);
801 dc.SetBrush(brush);
802 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
803 }
804 }
805
603f702b 806 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
807 {
808 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
914a4e23 809 wxColour col(attr.GetBottom().GetColour());
bec80f4f
JS
810
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom == 1)
813 {
814 int penStyle = wxSOLID;
815 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
816 penStyle = wxDOT;
817 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818 penStyle = wxLONG_DASH;
603f702b 819 wxPen pen(col, 1, penStyle);
bec80f4f
JS
820 dc.SetPen(pen);
821 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
822
823 }
824 else if (borderBottom > 1)
825 {
826 wxPen pen(col);
827 wxBrush brush(col);
828 dc.SetPen(pen);
829 dc.SetBrush(brush);
63af79de 830 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
bec80f4f
JS
831 }
832 }
833
834 return true;
835}
836
837// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838// or marginRect (outer), and the other must be the default rectangle (no width or height).
839// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
840// is available.
841//
842// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843
603f702b 844bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
bec80f4f
JS
845{
846 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
850
603f702b 851 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 852
603f702b 853 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f 854 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
603f702b 855 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
bec80f4f 856 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
603f702b 857 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
bec80f4f 858 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
83c6ae8e 859 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
bec80f4f
JS
860 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
861
603f702b 862 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f 863 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
603f702b 864 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
bec80f4f 865 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
603f702b 866 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
bec80f4f 867 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
83c6ae8e 868 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
bec80f4f
JS
869 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870
603f702b 871 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
bec80f4f 872 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
603f702b 873 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
bec80f4f 874 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
603f702b 875 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
bec80f4f 876 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b 877 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
bec80f4f
JS
878 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
879
603f702b 880 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
bec80f4f 881 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
603f702b 882 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
bec80f4f 883 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
603f702b 884 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
bec80f4f 885 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
603f702b 886 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
bec80f4f
JS
887 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888
889 int leftTotal = marginLeft + borderLeft + paddingLeft;
890 int rightTotal = marginRight + borderRight + paddingRight;
891 int topTotal = marginTop + borderTop + paddingTop;
892 int bottomTotal = marginBottom + borderBottom + paddingBottom;
893
894 if (marginRect != wxRect())
895 {
896 contentRect.x = marginRect.x + leftTotal;
897 contentRect.y = marginRect.y + topTotal;
898 contentRect.width = marginRect.width - (leftTotal + rightTotal);
899 contentRect.height = marginRect.height - (topTotal + bottomTotal);
900 }
901 else
902 {
903 marginRect.x = contentRect.x - leftTotal;
904 marginRect.y = contentRect.y - topTotal;
905 marginRect.width = contentRect.width + (leftTotal + rightTotal);
906 marginRect.height = contentRect.height + (topTotal + bottomTotal);
907 }
908
909 borderRect.x = marginRect.x + marginLeft;
910 borderRect.y = marginRect.y + marginTop;
911 borderRect.width = marginRect.width - (marginLeft + marginRight);
912 borderRect.height = marginRect.height - (marginTop + marginBottom);
913
914 paddingRect.x = marginRect.x + marginLeft + borderLeft;
915 paddingRect.y = marginRect.y + marginTop + borderTop;
916 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
918
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect.x = marginRect.x - outlineLeft;
921 outlineRect.y = marginRect.y - outlineTop;
922 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
924
925 return true;
926}
927
603f702b
JS
928// Get the total margin for the object in pixels, taking into account margin, padding and border size
929bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930 int& topMargin, int& bottomMargin)
931{
932 // Assume boxRect is the area around the content
933 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934 marginRect = wxRect(0, 0, 1000, 1000);
bec80f4f 935
603f702b
JS
936 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
937
938 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939 rightMargin = marginRect.GetRight() - contentRect.GetRight();
940 topMargin = contentRect.GetTop() - marginRect.GetTop();
941 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
942
943 return true;
944}
945
946// Returns the rectangle which the child has available to it given restrictions specified in the
947// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
bb7bbd12
JS
948// availableContainerSpace might be a parent that the cell has to compute its width relative to.
949// E.g. a cell that's 50% of its parent.
950wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
603f702b
JS
951{
952 wxRect rect = availableParentSpace;
953 double scale = 1.0;
954 if (buffer)
955 scale = buffer->GetScale();
956
bb7bbd12 957 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
603f702b
JS
958
959 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
961
962 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
964
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 {
969 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
970 }
971 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 {
973 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 975 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
603f702b
JS
976 else
977 rect.x += x;
978 }
979
980 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 {
982 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
983 }
984 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 {
986 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 988 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
603f702b
JS
989 else
990 rect.y += y;
991 }
992
bb7bbd12
JS
993 if (rect.GetWidth() > availableParentSpace.GetWidth())
994 rect.SetWidth(availableParentSpace.GetWidth());
995
603f702b
JS
996 return rect;
997}
998
999// Dump to output stream for debugging
5d7836c4
JS
1000void wxRichTextObject::Dump(wxTextOutputStream& stream)
1001{
1002 stream << GetClassInfo()->GetClassName() << wxT("\n");
1003 stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
1004 stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
1005}
1006
603f702b 1007// Gets the containing buffer
44219ff0
JS
1008wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1009{
1010 const wxRichTextObject* obj = this;
345c78ca 1011 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
44219ff0
JS
1012 obj = obj->GetParent();
1013 return wxDynamicCast(obj, wxRichTextBuffer);
1014}
5d7836c4 1015
603f702b
JS
1016// Get the absolute object position, by traversing up the child/parent hierarchy
1017wxPoint wxRichTextObject::GetAbsolutePosition() const
1018{
1019 wxPoint pt = GetPosition();
1020
1021 wxRichTextObject* p = GetParent();
1022 while (p)
1023 {
1024 pt = pt + p->GetPosition();
1025 p = p->GetParent();
1026 }
1027
1028 return pt;
1029}
1030
1031// Hit-testing: returns a flag indicating hit test details, plus
1032// information about position
8db2e3ef 1033int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
603f702b
JS
1034{
1035 if (!IsShown())
1036 return wxRICHTEXT_HITTEST_NONE;
1037
1038 wxRect rect = GetRect();
1039 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040 pt.y >= rect.y && pt.y < rect.y + rect.height)
1041 {
1042 *obj = this;
1043 *contextObj = GetParentContainer();
1044 textPosition = GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON;
1046 }
1047 else
1048 return wxRICHTEXT_HITTEST_NONE;
1049}
1050
1051// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052// lays out the object again using the maximum ('best') size
8db2e3ef 1053bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
bb7bbd12
JS
1054 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
603f702b
JS
1056 int style)
1057{
bb7bbd12 1058 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
603f702b 1059 wxRect originalAvailableRect = availableChildRect;
8db2e3ef 1060 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1061
1062 wxSize maxSize = GetMaxSize();
1063
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 // on this basis
bb7bbd12 1066 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
603f702b
JS
1067 {
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL);
1070 wxRichTextAttr newAttr(attr);
1071 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1073
bb7bbd12 1074 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
603f702b
JS
1075
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
e12b91a3
JS
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
603f702b
JS
1081 {
1082 // centering, right-justification
8db2e3ef 1083 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
1084 {
1085 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1086 }
8db2e3ef 1087 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
1088 {
1089 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1090 }
1091 }
1092
8db2e3ef 1093 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1094 }
1095
1096 /*
1097 __________________
1098 | ____________ |
1099 | | | |
1100
1101
1102 */
1103
1104 return true;
1105}
1106
1107// Move the object recursively, by adding the offset from old to new
1108void wxRichTextObject::Move(const wxPoint& pt)
1109{
1110 SetPosition(pt);
1111}
1112
1113
5d7836c4
JS
1114/*!
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1117 */
1118
1119IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1120
1121wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1122 wxRichTextObject(parent)
1123{
1124}
1125
1126wxRichTextCompositeObject::~wxRichTextCompositeObject()
1127{
1128 DeleteChildren();
1129}
1130
1131/// Get the nth child
1132wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1133{
1134 wxASSERT ( n < m_children.GetCount() );
1135
1136 return m_children.Item(n)->GetData();
1137}
1138
1139/// Append a child, returning the position
1140size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1141{
1142 m_children.Append(child);
1143 child->SetParent(this);
1144 return m_children.GetCount() - 1;
1145}
1146
1147/// Insert the child in front of the given object, or at the beginning
1148bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1149{
1150 if (inFrontOf)
1151 {
1152 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1153 m_children.Insert(node, child);
1154 }
1155 else
1156 m_children.Insert(child);
1157 child->SetParent(this);
1158
1159 return true;
1160}
1161
1162/// Delete the child
1163bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1164{
1165 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1166 if (node)
1167 {
efbf6735
JS
1168 wxRichTextObject* obj = node->GetData();
1169 m_children.Erase(node);
5d7836c4 1170 if (deleteChild)
efbf6735 1171 delete obj;
5d7836c4
JS
1172
1173 return true;
1174 }
1175 return false;
1176}
1177
1178/// Delete all children
1179bool wxRichTextCompositeObject::DeleteChildren()
1180{
1181 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1182 while (node)
1183 {
1184 wxRichTextObjectList::compatibility_iterator oldNode = node;
1185
1186 wxRichTextObject* child = node->GetData();
1187 child->Dereference(); // Only delete if reference count is zero
1188
1189 node = node->GetNext();
efbf6735 1190 m_children.Erase(oldNode);
5d7836c4
JS
1191 }
1192
1193 return true;
1194}
1195
1196/// Get the child count
1197size_t wxRichTextCompositeObject::GetChildCount() const
1198{
1199 return m_children.GetCount();
1200}
1201
1202/// Copy
1203void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1204{
1205 wxRichTextObject::Copy(obj);
1206
1207 DeleteChildren();
1208
1209 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1210 while (node)
1211 {
1212 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
1213 wxRichTextObject* newChild = child->Clone();
1214 newChild->SetParent(this);
1215 m_children.Append(newChild);
5d7836c4
JS
1216
1217 node = node->GetNext();
1218 }
1219}
1220
1221/// Hit-testing: returns a flag indicating hit test details, plus
1222/// information about position
8db2e3ef 1223int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 1224{
603f702b
JS
1225 if (!IsShown())
1226 return wxRICHTEXT_HITTEST_NONE;
1227
5d7836c4
JS
1228 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1229 while (node)
1230 {
1231 wxRichTextObject* child = node->GetData();
1232
603f702b
JS
1233 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1234 {
1235 // Just check if we hit the overall object
8db2e3ef 1236 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1237 if (ret != wxRICHTEXT_HITTEST_NONE)
1238 return ret;
1239 }
1240 else if (child->IsShown())
1241 {
8db2e3ef 1242 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1243 if (ret != wxRICHTEXT_HITTEST_NONE)
1244 return ret;
1245 }
5d7836c4
JS
1246
1247 node = node->GetNext();
1248 }
1249
603f702b 1250 return wxRICHTEXT_HITTEST_NONE;
5d7836c4
JS
1251}
1252
1253/// Finds the absolute position and row height for the given character position
8db2e3ef 1254bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
1255{
1256 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1257 while (node)
1258 {
1259 wxRichTextObject* child = node->GetData();
1260
603f702b
JS
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
8db2e3ef 1265 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
5d7836c4
JS
1266 return true;
1267
1268 node = node->GetNext();
1269 }
1270
1271 return false;
1272}
1273
1274/// Calculate range
1275void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1276{
1277 long current = start;
1278 long lastEnd = current;
1279
603f702b
JS
1280 if (IsTopLevel())
1281 {
1282 current = 0;
1283 lastEnd = 0;
1284 }
1285
5d7836c4
JS
1286 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1287 while (node)
1288 {
1289 wxRichTextObject* child = node->GetData();
1290 long childEnd = 0;
1291
1292 child->CalculateRange(current, childEnd);
1293 lastEnd = childEnd;
1294
1295 current = childEnd + 1;
1296
1297 node = node->GetNext();
1298 }
1299
603f702b
JS
1300 if (IsTopLevel())
1301 {
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1304 end = start;
1305 m_range.SetRange(start, start);
5d7836c4 1306
603f702b
JS
1307 // An object with no children has zero length
1308 if (m_children.GetCount() == 0)
1309 lastEnd --;
1310 m_ownRange.SetRange(0, lastEnd);
1311 }
1312 else
1313 {
1314 end = lastEnd;
5d7836c4 1315
603f702b
JS
1316 // An object with no children has zero length
1317 if (m_children.GetCount() == 0)
1318 end --;
1319
1320 m_range.SetRange(start, end);
1321 }
5d7836c4
JS
1322}
1323
1324/// Delete range from layout.
1325bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1326{
1327 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1328
5d7836c4
JS
1329 while (node)
1330 {
1331 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1332 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1333
5d7836c4
JS
1334 // Delete the range in each paragraph
1335
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
7fe8059f 1342
5d7836c4
JS
1343 if (!obj->GetRange().IsOutside(range))
1344 {
603f702b
JS
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj->IsTopLevel())
1347 obj->DeleteRange(range);
5d7836c4
JS
1348
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj->IsEmpty() ||
1351 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1352 {
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
7fe8059f 1355 RemoveChild(obj, true);
5d7836c4
JS
1356 }
1357 }
7fe8059f 1358
5d7836c4
JS
1359 node = next;
1360 }
7fe8059f 1361
5d7836c4
JS
1362 return true;
1363}
1364
1365/// Get any text in this object for the given range
1366wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1367{
1368 wxString text;
1369 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1370 while (node)
1371 {
1372 wxRichTextObject* child = node->GetData();
1373 wxRichTextRange childRange = range;
1374 if (!child->GetRange().IsOutside(range))
1375 {
1376 childRange.LimitTo(child->GetRange());
7fe8059f 1377
5d7836c4 1378 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1379
5d7836c4
JS
1380 text += childText;
1381 }
1382 node = node->GetNext();
1383 }
1384
1385 return text;
1386}
1387
603f702b
JS
1388/// Get the child object at the given character position
1389wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1390{
1391 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1392 while (node)
1393 {
1394 wxRichTextObject* child = node->GetData();
1395 if (child->GetRange().GetStart() == pos)
1396 return child;
1397 node = node->GetNext();
1398 }
1399 return NULL;
1400}
1401
5d7836c4 1402/// Recursively merge all pieces that can be merged.
f7667b84 1403bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range)
5d7836c4
JS
1404{
1405 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1406 while (node)
1407 {
1408 wxRichTextObject* child = node->GetData();
5cb0b827 1409 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
5d7836c4 1410 {
109bfc88
JS
1411 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1412 if (composite)
f7667b84 1413 composite->Defragment(context);
109bfc88 1414
f7667b84
JS
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context.GetVirtualAttributesEnabled())
5d7836c4 1419 {
f7667b84 1420 if (node->GetNext())
109bfc88 1421 {
f7667b84
JS
1422 wxRichTextObject* nextChild = node->GetNext()->GetData();
1423 if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
1424 {
1425 nextChild->Dereference();
1426 m_children.Erase(node->GetNext());
1427 }
1428 else
1429 node = node->GetNext();
109bfc88
JS
1430 }
1431 else
1432 node = node->GetNext();
5d7836c4
JS
1433 }
1434 else
f7667b84
JS
1435 {
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1439 // at a time.
1440 wxRichTextObject* childAfterSplit = child;
1441 if (child->CanSplit(context))
1442 {
1443 childAfterSplit = child->Split(context);
1444 node = m_children.Find(childAfterSplit);
1445 }
1446
1447 if (node->GetNext())
1448 {
1449 wxRichTextObject* nextChild = node->GetNext()->GetData();
f7667b84
JS
1450
1451 // First split child and nextChild so we have smaller fragments to merge.
1452 // Then Merge only has to test per-object virtual attributes
1453 // because for an object with all the same sub-object attributes,
1454 // then any general virtual attributes should be merged with sub-objects by
1455 // the implementation.
1456
1457 wxRichTextObject* nextChildAfterSplit = nextChild;
1458
1459 if (nextChildAfterSplit->CanSplit(context))
1460 nextChildAfterSplit = nextChild->Split(context);
1461
1462 bool splitNextChild = nextChild != nextChildAfterSplit;
1463
1464 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1465 // Note that we use nextChild because if we had split nextChild, the first object always
1466 // remains (and further parts are appended). However we must use childAfterSplit since
1467 // it's the last part of a possibly split child.
1468
1469 if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
1470 {
1471 nextChild->Dereference();
1472 m_children.Erase(node->GetNext());
1473
1474 // Don't set node -- we'll see if we can merge again with the next
1475 // child. UNLESS we split this or the next child, in which case we know we have to
1476 // move on to the end of the next child.
1477 if (splitNextChild)
1478 node = m_children.Find(nextChildAfterSplit);
1479 }
1480 else
1481 {
1482 if (splitNextChild)
1483 node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
1484 else
1485 node = node->GetNext();
1486 }
1487 }
1488 else
1489 node = node->GetNext();
1490 }
5d7836c4
JS
1491 }
1492 else
1493 node = node->GetNext();
1494 }
1495
bec80f4f
JS
1496 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1497 if (GetChildCount() > 1)
5d7836c4 1498 {
bec80f4f
JS
1499 node = m_children.GetFirst();
1500 while (node)
1501 {
1502 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1503 wxRichTextObject* child = node->GetData();
1504 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1505 {
1506 if (child->IsEmpty())
1507 {
1508 child->Dereference();
1509 m_children.Erase(node);
1510 }
1511 node = next;
1512 }
1513 else
1514 node = node->GetNext();
1515 }
5d7836c4 1516 }
5d7836c4 1517
5d7836c4
JS
1518 return true;
1519}
1520
bec80f4f
JS
1521/// Dump to output stream for debugging
1522void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1523{
1524 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1525 while (node)
1526 {
1527 wxRichTextObject* child = node->GetData();
bec80f4f 1528 child->Dump(stream);
5d7836c4
JS
1529 node = node->GetNext();
1530 }
5d7836c4
JS
1531}
1532
603f702b
JS
1533/// Get/set the object size for the given range. Returns false if the range
1534/// is invalid for this object.
914a4e23 1535bool 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
1536{
1537 if (!range.IsWithin(GetRange()))
1538 return false;
5d7836c4 1539
603f702b 1540 wxSize sz;
5d7836c4 1541
603f702b
JS
1542 wxArrayInt childExtents;
1543 wxArrayInt* p;
1544 if (partialExtents)
1545 p = & childExtents;
1546 else
1547 p = NULL;
5d7836c4 1548
603f702b
JS
1549 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1550 while (node)
cdaed652 1551 {
603f702b
JS
1552 wxRichTextObject* child = node->GetData();
1553 if (!child->GetRange().IsOutside(range))
1554 {
1555 // Floating objects have a zero size within the paragraph.
e12b91a3 1556 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
603f702b
JS
1557 {
1558 if (partialExtents)
1559 {
1560 int lastSize;
1561 if (partialExtents->GetCount() > 0)
1562 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1563 else
1564 lastSize = 0;
cdaed652 1565
603f702b
JS
1566 partialExtents->Add(0 /* zero size */ + lastSize);
1567 }
1568 }
1569 else
1570 {
1571 wxSize childSize;
5d7836c4 1572
603f702b
JS
1573 wxRichTextRange rangeToUse = range;
1574 rangeToUse.LimitTo(child->GetRange());
1575 if (child->IsTopLevel())
1576 rangeToUse = child->GetOwnRange();
5d7836c4 1577
603f702b 1578 int childDescent = 0;
cdaed652 1579
603f702b
JS
1580 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1581 // but it's only going to be used after caching has taken place.
1582 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1583 {
1584 childDescent = child->GetDescent();
1585 childSize = child->GetCachedSize();
bec80f4f 1586
603f702b
JS
1587 sz.y = wxMax(sz.y, childSize.y);
1588 sz.x += childSize.x;
1589 descent = wxMax(descent, childDescent);
1590 }
914a4e23 1591 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
603f702b
JS
1592 {
1593 sz.y = wxMax(sz.y, childSize.y);
1594 sz.x += childSize.x;
1595 descent = wxMax(descent, childDescent);
bec80f4f 1596
603f702b
JS
1597 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1598 {
1599 child->SetCachedSize(childSize);
1600 child->SetDescent(childDescent);
1601 }
bec80f4f 1602
603f702b
JS
1603 if (partialExtents)
1604 {
1605 int lastSize;
1606 if (partialExtents->GetCount() > 0)
1607 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1608 else
1609 lastSize = 0;
bec80f4f 1610
603f702b
JS
1611 size_t i;
1612 for (i = 0; i < childExtents.GetCount(); i++)
1613 {
1614 partialExtents->Add(childExtents[i] + lastSize);
1615 }
1616 }
1617 }
1618 }
1619
1620 if (p)
1621 p->Clear();
1622 }
1623
1624 node = node->GetNext();
1625 }
1626 size = sz;
1627 return true;
1628}
1629
1630// Invalidate the buffer. With no argument, invalidates whole buffer.
1631void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1632{
1633 wxRichTextObject::Invalidate(invalidRange);
1634
1635 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1636 while (node)
1637 {
1638 wxRichTextObject* child = node->GetData();
1639 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1640 {
1641 // Skip
1642 }
1643 else if (child->IsTopLevel())
1644 {
e12b91a3 1645 if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
c4168888
JS
1646 {
1647 // Don't invalidate subhierarchy if we've already been laid out
1648 }
603f702b 1649 else
c4168888
JS
1650 {
1651 if (invalidRange == wxRICHTEXT_NONE)
1652 child->Invalidate(wxRICHTEXT_NONE);
1653 else
1654 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1655 }
603f702b
JS
1656 }
1657 else
1658 child->Invalidate(invalidRange);
1659 node = node->GetNext();
1660 }
1661}
1662
1663// Move the object recursively, by adding the offset from old to new
1664void wxRichTextCompositeObject::Move(const wxPoint& pt)
1665{
1666 wxPoint oldPos = GetPosition();
1667 SetPosition(pt);
1668 wxPoint offset = pt - oldPos;
1669
1670 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1671 while (node)
1672 {
1673 wxRichTextObject* child = node->GetData();
1674 wxPoint childPos = child->GetPosition() + offset;
1675 child->Move(childPos);
1676 node = node->GetNext();
1677 }
1678}
1679
1680
1681/*!
1682 * wxRichTextParagraphLayoutBox
1683 * This box knows how to lay out paragraphs.
1684 */
1685
1686IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1687
1688wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1689 wxRichTextCompositeObject(parent)
1690{
1691 Init();
1692}
1693
1694wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1695{
1696 if (m_floatCollector)
1697 {
1698 delete m_floatCollector;
1699 m_floatCollector = NULL;
1700 }
1701}
1702
1703/// Initialize the object.
1704void wxRichTextParagraphLayoutBox::Init()
1705{
1706 m_ctrl = NULL;
1707
1708 // For now, assume is the only box and has no initial size.
1709 m_range = wxRichTextRange(0, -1);
1710 m_ownRange = wxRichTextRange(0, -1);
1711
1712 m_invalidRange = wxRICHTEXT_ALL;
1713
603f702b
JS
1714 m_partialParagraph = false;
1715 m_floatCollector = NULL;
1716}
1717
1718void wxRichTextParagraphLayoutBox::Clear()
1719{
1720 DeleteChildren();
1721
1722 if (m_floatCollector)
1723 delete m_floatCollector;
1724 m_floatCollector = NULL;
1725 m_partialParagraph = false;
1726}
1727
1728/// Copy
1729void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1730{
1731 Clear();
1732
1733 wxRichTextCompositeObject::Copy(obj);
1734
1735 m_partialParagraph = obj.m_partialParagraph;
1736 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1737}
1738
07d4142f
JS
1739// Gather information about floating objects; only gather floats for those paragraphs that
1740// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1741// during layout.
603f702b 1742bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1743{
1744 if (m_floatCollector != NULL)
1745 delete m_floatCollector;
603f702b 1746 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652 1747 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
07d4142f
JS
1748 // Only gather floats up to the point we'll start formatting paragraphs.
1749 while (untilObj && node && node->GetData() != untilObj)
cdaed652
VZ
1750 {
1751 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1752 wxASSERT (child != NULL);
1753 if (child)
1754 m_floatCollector->CollectFloat(child);
1755 node = node->GetNext();
1756 }
ce00f59b 1757
cdaed652
VZ
1758 return true;
1759}
1760
603f702b
JS
1761// Returns the style sheet associated with the overall buffer.
1762wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1763{
1764 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1765}
1766
1767// Get the number of floating objects at this level
1768int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1769{
1770 if (m_floatCollector)
1771 return m_floatCollector->GetFloatingObjectCount();
1772 else
1773 return 0;
1774}
1775
1776// Get a list of floating objects
1777bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1778{
1779 if (m_floatCollector)
1780 {
1781 return m_floatCollector->GetFloatingObjects(objects);
1782 }
1783 else
1784 return false;
1785}
1786
1787// Calculate ranges
1788void wxRichTextParagraphLayoutBox::UpdateRanges()
1789{
1790 long start = 0;
1791 if (GetParent())
1792 start = GetRange().GetStart();
1793 long end;
1794 CalculateRange(start, end);
1795}
1796
cdaed652 1797// HitTest
8db2e3ef 1798int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1799{
603f702b
JS
1800 if (!IsShown())
1801 return wxRICHTEXT_HITTEST_NONE;
1802
cdaed652 1803 int ret = wxRICHTEXT_HITTEST_NONE;
e12b91a3 1804 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
8db2e3ef 1805 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
ce00f59b 1806
cdaed652 1807 if (ret == wxRICHTEXT_HITTEST_NONE)
8db2e3ef 1808 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
cdaed652 1809 else
603f702b
JS
1810 {
1811 *contextObj = this;
cdaed652 1812 return ret;
603f702b 1813 }
cdaed652
VZ
1814}
1815
1816/// Draw the floating objects
8db2e3ef 1817void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652 1818{
e12b91a3 1819 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
8db2e3ef 1820 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
cdaed652
VZ
1821}
1822
bec80f4f 1823void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1824{
1825 if (from == to)
1826 return;
1827
1828 from->RemoveChild(obj);
1829 to->AppendChild(obj);
5d7836c4
JS
1830}
1831
1832/// Draw the item
8db2e3ef 1833bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1834{
603f702b
JS
1835 if (!IsShown())
1836 return true;
1837
1838 wxRect thisRect(GetPosition(), GetCachedSize());
1839
8db2e3ef
JS
1840 wxRichTextAttr attr(GetAttributes());
1841 context.ApplyVirtualAttributes(attr, this);
1842
603f702b
JS
1843 int flags = style;
1844 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1845 flags |= wxRICHTEXT_DRAW_SELECTED;
1846
1847 // Don't draw guidelines if at top level
1848 int theseFlags = flags;
1849 if (!GetParent())
1850 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
8db2e3ef 1851 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
603f702b 1852
e12b91a3
JS
1853 if (wxRichTextBuffer::GetFloatingLayoutMode())
1854 DrawFloats(dc, context, range, selection, rect, descent, style);
1855
5d7836c4
JS
1856 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1857 while (node)
1858 {
603f702b 1859 wxRichTextObject* child = node->GetData();
7fe8059f 1860
5d7836c4
JS
1861 if (child && !child->GetRange().IsOutside(range))
1862 {
1863 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1864 wxRichTextRange childRange = range;
1865 if (child->IsTopLevel())
1866 {
1867 childRange = child->GetOwnRange();
1868 }
7fe8059f 1869
ea160b2e
JS
1870 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1871 {
1872 // Stop drawing
1873 break;
1874 }
1875 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1876 {
1877 // Skip
1878 }
1879 else
8db2e3ef 1880 child->Draw(dc, context, childRange, selection, rect, descent, style);
5d7836c4
JS
1881 }
1882
1883 node = node->GetNext();
1884 }
1885 return true;
1886}
1887
1888/// Lay the item out
8db2e3ef 1889bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 1890{
603f702b
JS
1891 SetPosition(rect.GetPosition());
1892
1893 if (!IsShown())
1894 return true;
1895
4d551ad5
JS
1896 wxRect availableSpace;
1897 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1898
8db2e3ef
JS
1899 wxRichTextAttr attr(GetAttributes());
1900 context.ApplyVirtualAttributes(attr, this);
1901
4d551ad5 1902 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1903 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1904 // so that during a size, only the visible part will be relaid out, or
1905 // it would take too long causing flicker. As an approximation, we assume that
1906 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1907 if (formatRect)
1908 {
603f702b 1909 wxRect rect2(0, 0, rect.width, rect.height);
8db2e3ef 1910 availableSpace = GetAvailableContentArea(dc, context, rect2);
4d551ad5
JS
1911
1912 // Invalidate the part of the buffer from the first visible line
1913 // to the end. If other parts of the buffer are currently invalid,
1914 // then they too will be taken into account if they are above
1915 // the visible point.
1916 long startPos = 0;
1917 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1918 if (line)
1919 startPos = line->GetAbsoluteRange().GetStart();
1920
603f702b 1921 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1922 }
1923 else
603f702b 1924 {
8db2e3ef 1925 availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
1926 }
1927
d157d142
JS
1928 // Fix the width if we're at the top level
1929 if (!GetParent())
1930 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1931
603f702b 1932 int leftMargin, rightMargin, topMargin, bottomMargin;
8db2e3ef 1933 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
603f702b 1934 topMargin, bottomMargin);
5d7836c4
JS
1935
1936 int maxWidth = 0;
603f702b
JS
1937 int maxHeight = 0;
1938
1939 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1940 int maxMaxWidth = 0;
1941
1942 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1943 int maxMinWidth = 0;
1944
1945 // If we have vertical alignment, we must recalculate everything.
8db2e3ef
JS
1946 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1947 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1948
5d7836c4 1949 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1950
38113684 1951 bool layoutAll = true;
1e967276 1952
38113684
JS
1953 // Get invalid range, rounding to paragraph start/end.
1954 wxRichTextRange invalidRange = GetInvalidRange(true);
1955
4d551ad5 1956 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1957 return true;
1958
603f702b 1959 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1960 layoutAll = true;
38113684 1961 else // If we know what range is affected, start laying out from that point on.
603f702b 1962 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1963 {
38113684 1964 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1965 if (firstParagraph)
1966 {
1967 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1968 wxRichTextObjectList::compatibility_iterator previousNode;
1969 if ( firstNode )
1970 previousNode = firstNode->GetPrevious();
9b4af7b7 1971 if (firstNode)
2c375f42 1972 {
9b4af7b7
JS
1973 if (previousNode)
1974 {
1975 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1976 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1977 }
7fe8059f 1978
2c375f42
JS
1979 // Now we're going to start iterating from the first affected paragraph.
1980 node = firstNode;
1e967276
JS
1981
1982 layoutAll = false;
2c375f42
JS
1983 }
1984 }
1985 }
1986
07d4142f
JS
1987 // Gather information about only those floating objects that will not be formatted,
1988 // after which floats will be gathered per-paragraph during layout.
e12b91a3
JS
1989 if (wxRichTextBuffer::GetFloatingLayoutMode())
1990 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1991
4d551ad5
JS
1992 // A way to force speedy rest-of-buffer layout (the 'else' below)
1993 bool forceQuickLayout = false;
39a1c2f2 1994
d3f6b1b5
JS
1995 // First get the size of the paragraphs we won't be laying out
1996 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1997 while (n && n != node)
1998 {
1999 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
2000 if (child)
2001 {
2002 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2003 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2004 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2005 }
2006 n = n->GetNext();
2007 }
2008
5d7836c4
JS
2009 while (node)
2010 {
2011 // Assume this box only contains paragraphs
2012
2013 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
706465df
JS
2014 // Unsure if this is needed
2015 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 2016
603f702b 2017 if (child && child->IsShown())
2c375f42 2018 {
603f702b
JS
2019 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2020 if ( !forceQuickLayout &&
2021 (layoutAll ||
2022 child->GetLines().IsEmpty() ||
2023 !child->GetRange().IsOutside(invalidRange)) )
2024 {
2025 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2026 // lays out the object again using the minimum size
8db2e3ef
JS
2027 child->LayoutToBestSize(dc, context, GetBuffer(),
2028 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2029
2030 // Layout must set the cached size
2031 availableSpace.y += child->GetCachedSize().y;
2032 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2033 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2034 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2035
2036 // If we're just formatting the visible part of the buffer,
2037 // and we're now past the bottom of the window, and we don't have any
2038 // floating objects (since they may cause wrapping to change for the rest of the
2039 // the buffer), start quick layout.
2040 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
2041 forceQuickLayout = true;
2042 }
2043 else
2044 {
2045 // We're outside the immediately affected range, so now let's just
2046 // move everything up or down. This assumes that all the children have previously
2047 // been laid out and have wrapped line lists associated with them.
2048 // TODO: check all paragraphs before the affected range.
2049
2050 int inc = availableSpace.y - child->GetPosition().y;
2051
2052 while (node)
2053 {
2054 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2055 if (child)
2056 {
2057 if (child->GetLines().GetCount() == 0)
2058 {
2059 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2060 // lays out the object again using the minimum size
8db2e3ef
JS
2061 child->LayoutToBestSize(dc, context, GetBuffer(),
2062 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2063
2064 //child->Layout(dc, availableChildRect, style);
2065 }
2066 else
2067 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 2068
603f702b
JS
2069 availableSpace.y += child->GetCachedSize().y;
2070 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2071 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2072 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2073 }
4d551ad5 2074
603f702b
JS
2075 node = node->GetNext();
2076 }
2077 break;
2078 }
2c375f42 2079 }
7fe8059f 2080
603f702b
JS
2081 node = node->GetNext();
2082 }
2083
2084 node = m_children.GetLast();
2085 if (node && node->GetData()->IsShown())
2086 {
2087 wxRichTextObject* child = node->GetData();
603f702b
JS
2088 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2089 }
2090 else
2091 maxHeight = 0; // topMargin + bottomMargin;
2092
23698b12 2093 // Check the bottom edge of any floating object
e12b91a3 2094 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
23698b12
JS
2095 {
2096 int bottom = GetFloatCollector()->GetLastRectBottom();
2097 if (bottom > maxHeight)
2098 maxHeight = bottom;
2099 }
2100
8db2e3ef 2101 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
bb7bbd12 2102 {
8db2e3ef 2103 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
bb7bbd12
JS
2104 int w = r.GetWidth();
2105
2106 // Convert external to content rect
2107 w = w - leftMargin - rightMargin;
2108 maxWidth = wxMax(maxWidth, w);
2109 maxMaxWidth = wxMax(maxMaxWidth, w);
2110 }
32423dd8
JS
2111 else
2112 {
2113 // TODO: Make sure the layout box's position reflects
2114 // the position of the children, but without
2115 // breaking layout of a box within a paragraph.
2116 }
bb7bbd12 2117
603f702b
JS
2118 // TODO: (also in para layout) should set the
2119 // object's size to an absolute one if specified,
2120 // but if not specified, calculate it from content.
2121
2122 // We need to add back the margins etc.
2123 {
2124 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2125 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
8db2e3ef 2126 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2127 SetCachedSize(marginRect.GetSize());
2128 }
2129
2130 // The maximum size is the greatest of all maximum widths for all paragraphs.
2131 {
2132 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2133 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2134 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2135 SetMaxSize(marginRect.GetSize());
2136 }
2137
2138 // The minimum size is the greatest of all minimum widths for all paragraphs.
2139 {
2140 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2141 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2142 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2143 SetMinSize(marginRect.GetSize());
2144 }
2145
8db2e3ef
JS
2146 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2147 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
603f702b
JS
2148 {
2149 int yOffset = 0;
2150 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2151 if (leftOverSpace > 0)
2152 {
8db2e3ef 2153 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
603f702b
JS
2154 {
2155 yOffset = (leftOverSpace/2);
2156 }
8db2e3ef 2157 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
603f702b
JS
2158 {
2159 yOffset = leftOverSpace;
2160 }
2161 }
7fe8059f 2162
603f702b
JS
2163 // Move all the children to vertically align the content
2164 // This doesn't take into account floating objects, unfortunately.
2165 if (yOffset != 0)
2166 {
2167 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
2168 while (node)
2169 {
2170 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2171 if (child)
603f702b 2172 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
2173
2174 node = node->GetNext();
2c375f42 2175 }
2c375f42 2176 }
5d7836c4
JS
2177 }
2178
1e967276 2179 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2180
2181 return true;
2182}
2183
5d7836c4 2184/// Get/set the size for the given range.
914a4e23 2185bool 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
2186{
2187 wxSize sz;
2188
09f14108
JS
2189 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2190 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2191
2192 // First find the first paragraph whose starting position is within the range.
2193 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2194 while (node)
2195 {
2196 // child is a paragraph
2197 wxRichTextObject* child = node->GetData();
2198 const wxRichTextRange& r = child->GetRange();
2199
2200 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2201 {
2202 startPara = node;
2203 break;
2204 }
2205
2206 node = node->GetNext();
2207 }
2208
2209 // Next find the last paragraph containing part of the range
2210 node = m_children.GetFirst();
2211 while (node)
2212 {
2213 // child is a paragraph
2214 wxRichTextObject* child = node->GetData();
2215 const wxRichTextRange& r = child->GetRange();
2216
2217 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2218 {
2219 endPara = node;
2220 break;
2221 }
2222
2223 node = node->GetNext();
2224 }
2225
2226 if (!startPara || !endPara)
2227 return false;
2228
2229 // Now we can add up the sizes
2230 for (node = startPara; node ; node = node->GetNext())
2231 {
2232 // child is a paragraph
2233 wxRichTextObject* child = node->GetData();
2234 const wxRichTextRange& childRange = child->GetRange();
2235 wxRichTextRange rangeToFind = range;
2236 rangeToFind.LimitTo(childRange);
2237
603f702b
JS
2238 if (child->IsTopLevel())
2239 rangeToFind = child->GetOwnRange();
2240
5d7836c4
JS
2241 wxSize childSize;
2242
2243 int childDescent = 0;
914a4e23 2244 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize);
5d7836c4
JS
2245
2246 descent = wxMax(childDescent, descent);
2247
2248 sz.x = wxMax(sz.x, childSize.x);
2249 sz.y += childSize.y;
2250
2251 if (node == endPara)
2252 break;
2253 }
2254
2255 size = sz;
2256
2257 return true;
2258}
2259
2260/// Get the paragraph at the given position
2261wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2262{
2263 if (caretPosition)
2264 pos ++;
2265
2266 // First find the first paragraph whose starting position is within the range.
2267 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2268 while (node)
2269 {
2270 // child is a paragraph
2271 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2272 // wxASSERT (child != NULL);
5d7836c4 2273
603f702b
JS
2274 if (child)
2275 {
2276 // Return first child in buffer if position is -1
2277 // if (pos == -1)
2278 // return child;
5d7836c4 2279
603f702b
JS
2280 if (child->GetRange().Contains(pos))
2281 return child;
2282 }
5d7836c4
JS
2283
2284 node = node->GetNext();
2285 }
2286 return NULL;
2287}
2288
2289/// Get the line at the given position
2290wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2291{
2292 if (caretPosition)
2293 pos ++;
2294
2295 // First find the first paragraph whose starting position is within the range.
2296 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2297 while (node)
2298 {
7051fa41
JS
2299 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2300 if (obj->GetRange().Contains(pos))
5d7836c4 2301 {
7051fa41
JS
2302 // child is a paragraph
2303 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2304 // wxASSERT (child != NULL);
7051fa41 2305
603f702b 2306 if (child)
7051fa41 2307 {
603f702b
JS
2308 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2309 while (node2)
2310 {
2311 wxRichTextLine* line = node2->GetData();
5d7836c4 2312
603f702b 2313 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2314
603f702b 2315 if (range.Contains(pos) ||
5d7836c4 2316
603f702b
JS
2317 // If the position is end-of-paragraph, then return the last line of
2318 // of the paragraph.
2319 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2320 return line;
5d7836c4 2321
603f702b
JS
2322 node2 = node2->GetNext();
2323 }
7051fa41 2324 }
7fe8059f 2325 }
5d7836c4
JS
2326
2327 node = node->GetNext();
2328 }
2329
2330 int lineCount = GetLineCount();
2331 if (lineCount > 0)
2332 return GetLineForVisibleLineNumber(lineCount-1);
2333 else
2334 return NULL;
2335}
2336
2337/// Get the line at the given y pixel position, or the last line.
2338wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2339{
2340 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2341 while (node)
2342 {
2343 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2344 // wxASSERT (child != NULL);
5d7836c4 2345
603f702b 2346 if (child)
5d7836c4 2347 {
603f702b
JS
2348 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2349 while (node2)
2350 {
2351 wxRichTextLine* line = node2->GetData();
5d7836c4 2352
603f702b 2353 wxRect rect(line->GetRect());
5d7836c4 2354
603f702b
JS
2355 if (y <= rect.GetBottom())
2356 return line;
5d7836c4 2357
603f702b
JS
2358 node2 = node2->GetNext();
2359 }
7fe8059f 2360 }
5d7836c4
JS
2361
2362 node = node->GetNext();
2363 }
2364
2365 // Return last line
2366 int lineCount = GetLineCount();
2367 if (lineCount > 0)
2368 return GetLineForVisibleLineNumber(lineCount-1);
2369 else
2370 return NULL;
2371}
2372
2373/// Get the number of visible lines
2374int wxRichTextParagraphLayoutBox::GetLineCount() const
2375{
2376 int count = 0;
2377
2378 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2379 while (node)
2380 {
2381 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2382 // wxASSERT (child != NULL);
2383
2384 if (child)
2385 count += child->GetLines().GetCount();
5d7836c4 2386
5d7836c4
JS
2387 node = node->GetNext();
2388 }
2389 return count;
2390}
2391
2392
2393/// Get the paragraph for a given line
2394wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2395{
1e967276 2396 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2397}
2398
2399/// Get the line size at the given position
2400wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2401{
2402 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2403 if (line)
2404 {
2405 return line->GetSize();
2406 }
2407 else
2408 return wxSize(0, 0);
2409}
2410
2411
2412/// Convenience function to add a paragraph of text
24777478 2413wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2414{
fe5aa22c 2415 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2416 // be combined at display time.
2417 // Divide into paragraph and character styles.
3e541562 2418
24777478
JS
2419 wxRichTextAttr defaultCharStyle;
2420 wxRichTextAttr defaultParaStyle;
4f32b3cf 2421
5607c890
JS
2422 // If the default style is a named paragraph style, don't apply any character formatting
2423 // to the initial text string.
2424 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2425 {
2426 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2427 if (def)
2428 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2429 }
2430 else
2431 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2432
24777478
JS
2433 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2434 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2435
2436 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
32423dd8 2437 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4
JS
2438
2439 AppendChild(para);
2440
2441 UpdateRanges();
5d7836c4
JS
2442
2443 return para->GetRange();
2444}
2445
2446/// Adds multiple paragraphs, based on newlines.
24777478 2447wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2448{
fe5aa22c 2449 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2450 // be combined at display time.
2451 // Divide into paragraph and character styles.
3e541562 2452
24777478
JS
2453 wxRichTextAttr defaultCharStyle;
2454 wxRichTextAttr defaultParaStyle;
5607c890
JS
2455
2456 // If the default style is a named paragraph style, don't apply any character formatting
2457 // to the initial text string.
2458 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2459 {
2460 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2461 if (def)
2462 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2463 }
2464 else
2465 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2466
24777478
JS
2467 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2468 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2469
5d7836c4
JS
2470 wxRichTextParagraph* firstPara = NULL;
2471 wxRichTextParagraph* lastPara = NULL;
2472
2473 wxRichTextRange range(-1, -1);
0ca07313 2474
5d7836c4 2475 size_t i = 0;
28f92d74 2476 size_t len = text.length();
5d7836c4 2477 wxString line;
4f32b3cf 2478 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2479 para->GetAttributes().GetTextBoxAttr().Reset();
0ca07313
JS
2480
2481 AppendChild(para);
2482
2483 firstPara = para;
2484 lastPara = para;
2485
5d7836c4
JS
2486 while (i < len)
2487 {
2488 wxChar ch = text[i];
2489 if (ch == wxT('\n') || ch == wxT('\r'))
2490 {
99404ab0
JS
2491 if (i != (len-1))
2492 {
2493 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2494 plainText->SetText(line);
0ca07313 2495
99404ab0 2496 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2497 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4 2498
99404ab0 2499 AppendChild(para);
0ca07313 2500
99404ab0
JS
2501 lastPara = para;
2502 line = wxEmptyString;
2503 }
5d7836c4
JS
2504 }
2505 else
2506 line += ch;
2507
2508 i ++;
2509 }
0ca07313 2510
7fe8059f 2511 if (!line.empty())
5d7836c4 2512 {
0ca07313
JS
2513 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2514 plainText->SetText(line);
5d7836c4
JS
2515 }
2516
5d7836c4 2517 UpdateRanges();
0ca07313 2518
0ca07313 2519 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2520}
2521
2522/// Convenience function to add an image
24777478 2523wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2524{
fe5aa22c 2525 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2526 // be combined at display time.
2527 // Divide into paragraph and character styles.
3e541562 2528
24777478
JS
2529 wxRichTextAttr defaultCharStyle;
2530 wxRichTextAttr defaultParaStyle;
5607c890
JS
2531
2532 // If the default style is a named paragraph style, don't apply any character formatting
2533 // to the initial text string.
2534 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2535 {
2536 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2537 if (def)
2538 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2539 }
2540 else
2541 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2542
24777478
JS
2543 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2544 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2545
4f32b3cf 2546 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
32423dd8 2547 para->GetAttributes().GetTextBoxAttr().Reset();
4f32b3cf
JS
2548 AppendChild(para);
2549 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2550
5d7836c4 2551 UpdateRanges();
5d7836c4
JS
2552
2553 return para->GetRange();
2554}
2555
2556
2557/// Insert fragment into this box at the given position. If partialParagraph is true,
2558/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2559/// marker.
5d7836c4 2560
0ca07313 2561bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2562{
5d7836c4
JS
2563 // First, find the first paragraph whose starting position is within the range.
2564 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2565 if (para)
2566 {
24777478 2567 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2568
5d7836c4
JS
2569 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2570
2571 // Now split at this position, returning the object to insert the new
2572 // ones in front of.
2573 wxRichTextObject* nextObject = para->SplitAt(position);
2574
2575 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2576 // text, for example, so let's optimize.
2577
2578 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2579 {
2580 // Add the first para to this para...
2581 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2582 if (!firstParaNode)
2583 return false;
2584
2585 // Iterate through the fragment paragraph inserting the content into this paragraph.
2586 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2587 wxASSERT (firstPara != NULL);
2588
2589 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2590 while (objectNode)
2591 {
2592 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2593
5d7836c4
JS
2594 if (!nextObject)
2595 {
2596 // Append
2597 para->AppendChild(newObj);
2598 }
2599 else
2600 {
2601 // Insert before nextObject
2602 para->InsertChild(newObj, nextObject);
2603 }
7fe8059f 2604
5d7836c4
JS
2605 objectNode = objectNode->GetNext();
2606 }
2607
2608 return true;
2609 }
2610 else
2611 {
2612 // Procedure for inserting a fragment consisting of a number of
2613 // paragraphs:
2614 //
2615 // 1. Remove and save the content that's after the insertion point, for adding
2616 // back once we've added the fragment.
2617 // 2. Add the content from the first fragment paragraph to the current
2618 // paragraph.
2619 // 3. Add remaining fragment paragraphs after the current paragraph.
2620 // 4. Add back the saved content from the first paragraph. If partialParagraph
2621 // is true, add it to the last paragraph added and not a new one.
2622
2623 // 1. Remove and save objects after split point.
2624 wxList savedObjects;
2625 if (nextObject)
2626 para->MoveToList(nextObject, savedObjects);
2627
2628 // 2. Add the content from the 1st fragment paragraph.
2629 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2630 if (!firstParaNode)
2631 return false;
2632
2633 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2634 wxASSERT(firstPara != NULL);
2635
6c0ea513
JS
2636 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2637 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2638
2639 // Save empty paragraph attributes for appending later
2640 // These are character attributes deliberately set for a new paragraph. Without this,
2641 // we couldn't pass default attributes when appending a new paragraph.
24777478 2642 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2643
5d7836c4 2644 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2645
2646 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2647 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2648
5d7836c4
JS
2649 while (objectNode)
2650 {
c025e094 2651 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2652
c025e094
JS
2653 // Append
2654 para->AppendChild(newObj);
7fe8059f 2655
5d7836c4
JS
2656 objectNode = objectNode->GetNext();
2657 }
2658
2659 // 3. Add remaining fragment paragraphs after the current paragraph.
2660 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2661 wxRichTextObject* nextParagraph = NULL;
2662 if (nextParagraphNode)
2663 nextParagraph = nextParagraphNode->GetData();
2664
2665 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2666 wxRichTextParagraph* finalPara = para;
2667
99404ab0
JS
2668 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2669
5d7836c4 2670 // If there was only one paragraph, we need to insert a new one.
99404ab0 2671 while (i)
5d7836c4 2672 {
99404ab0
JS
2673 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2674 wxASSERT( para != NULL );
5d7836c4 2675
99404ab0 2676 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2677
2678 if (nextParagraph)
2679 InsertChild(finalPara, nextParagraph);
2680 else
7fe8059f 2681 AppendChild(finalPara);
99404ab0
JS
2682
2683 i = i->GetNext();
5d7836c4 2684 }
5d7836c4 2685
99404ab0
JS
2686 // If there was only one paragraph, or we have full paragraphs in our fragment,
2687 // we need to insert a new one.
2688 if (needExtraPara)
2689 {
2690 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2691
2692 if (nextParagraph)
2693 InsertChild(finalPara, nextParagraph);
2694 else
2695 AppendChild(finalPara);
5d7836c4
JS
2696 }
2697
2698 // 4. Add back the remaining content.
2699 if (finalPara)
2700 {
c025e094
JS
2701 if (nextObject)
2702 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2703
2704 // Ensure there's at least one object
2705 if (finalPara->GetChildCount() == 0)
2706 {
7fe8059f 2707 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2708 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2709
2710 finalPara->AppendChild(text);
2711 }
2712 }
2713
6c0ea513
JS
2714 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2715 finalPara->SetAttributes(firstPara->GetAttributes());
2716 else if (finalPara && finalPara != para)
99404ab0
JS
2717 finalPara->SetAttributes(originalAttr);
2718
5d7836c4
JS
2719 return true;
2720 }
2721 }
2722 else
2723 {
2724 // Append
2725 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2726 while (i)
2727 {
2728 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2729 wxASSERT( para != NULL );
7fe8059f 2730
5d7836c4 2731 AppendChild(para->Clone());
7fe8059f 2732
5d7836c4
JS
2733 i = i->GetNext();
2734 }
2735
2736 return true;
2737 }
5d7836c4
JS
2738}
2739
2740/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2741/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2742bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2743{
2744 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2745 while (i)
2746 {
2747 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2748 wxASSERT( para != NULL );
2749
2750 if (!para->GetRange().IsOutside(range))
2751 {
2752 fragment.AppendChild(para->Clone());
7fe8059f 2753 }
5d7836c4
JS
2754 i = i->GetNext();
2755 }
2756
2757 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2758 if (!fragment.IsEmpty())
2759 {
5d7836c4
JS
2760 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2761 wxASSERT( firstPara != NULL );
2762
0e190fa2
JS
2763 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2764 wxASSERT( lastPara != NULL );
2765
2766 if (!firstPara || !lastPara)
2767 return false;
2768
2769 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2770
2771 long firstPos = firstPara->GetRange().GetStart();
2772
2773 // Adjust for renumbering from zero
2774 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2775
2776 long end;
2777 fragment.CalculateRange(0, end);
2778
5d7836c4 2779 // Chop off the start of the paragraph
0e190fa2 2780 if (topTailRange.GetStart() > 0)
5d7836c4 2781 {
0e190fa2 2782 wxRichTextRange r(0, topTailRange.GetStart()-1);
5d7836c4
JS
2783 firstPara->DeleteRange(r);
2784
2785 // Make sure the numbering is correct
0e190fa2 2786 fragment.CalculateRange(0, end);
5d7836c4
JS
2787
2788 // Now, we've deleted some positions, so adjust the range
2789 // accordingly.
0e190fa2
JS
2790 topTailRange.SetStart(range.GetLength());
2791 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2792 }
2793 else
2794 {
2795 topTailRange.SetStart(range.GetLength());
2796 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
5d7836c4
JS
2797 }
2798
61e6149e 2799 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
5d7836c4 2800 {
0e190fa2 2801 lastPara->DeleteRange(topTailRange);
5d7836c4
JS
2802
2803 // Make sure the numbering is correct
2804 long end;
0e190fa2 2805 fragment.CalculateRange(0, end);
5d7836c4
JS
2806
2807 // We only have part of a paragraph at the end
2808 fragment.SetPartialParagraph(true);
2809 }
2810 else
2811 {
0e190fa2
JS
2812 // We have a partial paragraph (don't save last new paragraph marker)
2813 // or complete paragraph
2814 fragment.SetPartialParagraph(isFragment);
5d7836c4
JS
2815 }
2816 }
2817
2818 return true;
2819}
2820
2821/// Given a position, get the number of the visible line (potentially many to a paragraph),
2822/// starting from zero at the start of the buffer.
2823long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2824{
2825 if (caretPosition)
2826 pos ++;
2827
2828 int lineCount = 0;
2829
2830 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2831 while (node)
2832 {
2833 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2834 // wxASSERT( child != NULL );
5d7836c4 2835
603f702b 2836 if (child)
5d7836c4 2837 {
603f702b 2838 if (child->GetRange().Contains(pos))
5d7836c4 2839 {
603f702b
JS
2840 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2841 while (node2)
5d7836c4 2842 {
603f702b
JS
2843 wxRichTextLine* line = node2->GetData();
2844 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2845
603f702b
JS
2846 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2847 {
2848 // If the caret is displayed at the end of the previous wrapped line,
2849 // we want to return the line it's _displayed_ at (not the actual line
2850 // containing the position).
2851 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2852 return lineCount - 1;
2853 else
2854 return lineCount;
2855 }
7fe8059f 2856
603f702b
JS
2857 lineCount ++;
2858
2859 node2 = node2->GetNext();
2860 }
2861 // If we didn't find it in the lines, it must be
2862 // the last position of the paragraph. So return the last line.
2863 return lineCount-1;
5d7836c4 2864 }
603f702b
JS
2865 else
2866 lineCount += child->GetLines().GetCount();
5d7836c4 2867 }
5d7836c4
JS
2868
2869 node = node->GetNext();
2870 }
2871
2872 // Not found
2873 return -1;
2874}
2875
2876/// Given a line number, get the corresponding wxRichTextLine object.
2877wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2878{
2879 int lineCount = 0;
2880
2881 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2882 while (node)
2883 {
2884 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2885 // wxASSERT(child != NULL);
5d7836c4 2886
603f702b 2887 if (child)
5d7836c4 2888 {
603f702b 2889 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2890 {
603f702b
JS
2891 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2892 while (node2)
2893 {
2894 wxRichTextLine* line = node2->GetData();
7fe8059f 2895
603f702b
JS
2896 if (lineCount == lineNumber)
2897 return line;
5d7836c4 2898
603f702b 2899 lineCount ++;
7fe8059f 2900
603f702b
JS
2901 node2 = node2->GetNext();
2902 }
7fe8059f 2903 }
603f702b
JS
2904 else
2905 lineCount += child->GetLines().GetCount();
5d7836c4 2906 }
5d7836c4
JS
2907
2908 node = node->GetNext();
2909 }
2910
2911 // Didn't find it
2912 return NULL;
2913}
2914
2915/// Delete range from layout.
2916bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2917{
2918 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2919
99404ab0 2920 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2921 while (node)
2922 {
2923 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2924 // wxASSERT (obj != NULL);
5d7836c4
JS
2925
2926 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2927
603f702b 2928 if (obj)
5d7836c4 2929 {
603f702b 2930 // Delete the range in each paragraph
99404ab0 2931
603f702b 2932 if (!obj->GetRange().IsOutside(range))
5d7836c4 2933 {
603f702b
JS
2934 // Deletes the content of this object within the given range
2935 obj->DeleteRange(range);
99404ab0 2936
603f702b
JS
2937 wxRichTextRange thisRange = obj->GetRange();
2938 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2939
603f702b
JS
2940 // If the whole paragraph is within the range to delete,
2941 // delete the whole thing.
2942 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2943 {
603f702b
JS
2944 // Delete the whole object
2945 RemoveChild(obj, true);
2946 obj = NULL;
99404ab0 2947 }
603f702b
JS
2948 else if (!firstPara)
2949 firstPara = obj;
5d7836c4 2950
603f702b
JS
2951 // If the range includes the paragraph end, we need to join this
2952 // and the next paragraph.
2953 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2954 {
603f702b
JS
2955 // We need to move the objects from the next paragraph
2956 // to this paragraph
2957
2958 wxRichTextParagraph* nextParagraph = NULL;
2959 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2960 nextParagraph = obj;
6c0ea513 2961 else
603f702b
JS
2962 {
2963 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2964 if (next)
2965 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2966 }
5d7836c4 2967
603f702b
JS
2968 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2969
2970 wxRichTextAttr nextParaAttr;
2971 if (applyFinalParagraphStyle)
2972 {
2973 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2974 // not the next one.
2975 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2976 nextParaAttr = thisAttr;
2977 else
2978 nextParaAttr = nextParagraph->GetAttributes();
2979 }
5d7836c4 2980
603f702b 2981 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2982 {
603f702b
JS
2983 // Move the objects to the previous para
2984 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2985
603f702b
JS
2986 while (node1)
2987 {
2988 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2989
603f702b 2990 firstPara->AppendChild(obj1);
5d7836c4 2991
603f702b
JS
2992 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2993 nextParagraph->GetChildren().Erase(node1);
99404ab0 2994
603f702b
JS
2995 node1 = next1;
2996 }
5d7836c4 2997
603f702b
JS
2998 // Delete the paragraph
2999 RemoveChild(nextParagraph, true);
3000 }
fa01bfdd 3001
603f702b
JS
3002 // Avoid empty paragraphs
3003 if (firstPara && firstPara->GetChildren().GetCount() == 0)
3004 {
3005 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
3006 firstPara->AppendChild(text);
3007 }
99404ab0 3008
603f702b
JS
3009 if (applyFinalParagraphStyle)
3010 firstPara->SetAttributes(nextParaAttr);
3011
3012 return true;
3013 }
5d7836c4
JS
3014 }
3015 }
7fe8059f 3016
5d7836c4
JS
3017 node = next;
3018 }
7fe8059f 3019
5d7836c4
JS
3020 return true;
3021}
3022
3023/// Get any text in this object for the given range
3024wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
3025{
3026 int lineCount = 0;
3027 wxString text;
3028 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3029 while (node)
3030 {
3031 wxRichTextObject* child = node->GetData();
3032 if (!child->GetRange().IsOutside(range))
3033 {
5d7836c4
JS
3034 wxRichTextRange childRange = range;
3035 childRange.LimitTo(child->GetRange());
7fe8059f 3036
5d7836c4 3037 wxString childText = child->GetTextForRange(childRange);
7fe8059f 3038
5d7836c4
JS
3039 text += childText;
3040
1a75935d 3041 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
3042 text += wxT("\n");
3043
5d7836c4
JS
3044 lineCount ++;
3045 }
3046 node = node->GetNext();
3047 }
3048
3049 return text;
3050}
3051
3052/// Get all the text
3053wxString wxRichTextParagraphLayoutBox::GetText() const
3054{
c99f1b0f 3055 return GetTextForRange(GetOwnRange());
5d7836c4
JS
3056}
3057
3058/// Get the paragraph by number
3059wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
3060{
27e20452 3061 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
3062 return NULL;
3063
3064 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
3065}
3066
3067/// Get the length of the paragraph
3068int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3069{
3070 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3071 if (para)
3072 return para->GetRange().GetLength() - 1; // don't include newline
3073 else
3074 return 0;
3075}
3076
3077/// Get the text of the paragraph
3078wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3079{
3080 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3081 if (para)
3082 return para->GetTextForRange(para->GetRange());
3083 else
3084 return wxEmptyString;
3085}
3086
3087/// Convert zero-based line column and paragraph number to a position.
3088long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3089{
3090 wxRichTextParagraph* para = GetParagraphAtLine(y);
3091 if (para)
3092 {
3093 return para->GetRange().GetStart() + x;
3094 }
3095 else
3096 return -1;
3097}
3098
3099/// Convert zero-based position to line column and paragraph number
3100bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3101{
3102 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3103 if (para)
3104 {
3105 int count = 0;
3106 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3107 while (node)
3108 {
3109 wxRichTextObject* child = node->GetData();
3110 if (child == para)
3111 break;
3112 count ++;
3113 node = node->GetNext();
3114 }
3115
3116 *y = count;
3117 *x = pos - para->GetRange().GetStart();
3118
3119 return true;
3120 }
3121 else
3122 return false;
3123}
3124
3125/// Get the leaf object in a paragraph at this position.
3126/// Given a line number, get the corresponding wxRichTextLine object.
3127wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3128{
3129 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3130 if (para)
3131 {
3132 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 3133
5d7836c4
JS
3134 while (node)
3135 {
3136 wxRichTextObject* child = node->GetData();
3137 if (child->GetRange().Contains(position))
3138 return child;
7fe8059f 3139
5d7836c4
JS
3140 node = node->GetNext();
3141 }
3142 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3143 return para->GetChildren().GetLast()->GetData();
3144 }
3145 return NULL;
3146}
3147
3148/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 3149bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
3150{
3151 bool characterStyle = false;
3152 bool paragraphStyle = false;
3153
3154 if (style.IsCharacterStyle())
3155 characterStyle = true;
3156 if (style.IsParagraphStyle())
3157 paragraphStyle = true;
3158
603f702b
JS
3159 wxRichTextBuffer* buffer = GetBuffer();
3160
59509217
JS
3161 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3162 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3163 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3164 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 3165 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 3166 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
3167
3168 // Apply paragraph style first, if any
24777478 3169 wxRichTextAttr wholeStyle(style);
523d2f14 3170
603f702b 3171 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 3172 {
603f702b 3173 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 3174 if (def)
603f702b 3175 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 3176 }
59509217
JS
3177
3178 // Limit the attributes to be set to the content to only character attributes.
24777478 3179 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
3180 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3181
603f702b 3182 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 3183 {
603f702b 3184 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 3185 if (def)
603f702b 3186 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
3187 }
3188
5d7836c4
JS
3189 // If we are associated with a control, make undoable; otherwise, apply immediately
3190 // to the data.
3191
603f702b 3192 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3193
3194 wxRichTextAction* action = NULL;
7fe8059f 3195
5d7836c4
JS
3196 if (haveControl && withUndo)
3197 {
603f702b 3198 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3199 action->SetRange(range);
603f702b 3200 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3201 }
3202
3203 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3204 while (node)
3205 {
3206 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3207 // wxASSERT (para != NULL);
5d7836c4
JS
3208
3209 if (para && para->GetChildCount() > 0)
3210 {
3211 // Stop searching if we're beyond the range of interest
3212 if (para->GetRange().GetStart() > range.GetEnd())
3213 break;
3214
3215 if (!para->GetRange().IsOutside(range))
3216 {
3217 // We'll be using a copy of the paragraph to make style changes,
3218 // not updating the buffer directly.
4e09ebe8 3219 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3220
5d7836c4
JS
3221 if (haveControl && withUndo)
3222 {
3223 newPara = new wxRichTextParagraph(*para);
3224 action->GetNewParagraphs().AppendChild(newPara);
3225
3226 // Also store the old ones for Undo
3227 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3228 }
3229 else
3230 newPara = para;
41a85215 3231
a7ed48a5
JS
3232 // If we're specifying paragraphs only, then we really mean character formatting
3233 // to be included in the paragraph style
3234 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3235 {
aeb6ebe2
JS
3236 if (removeStyle)
3237 {
3238 // Removes the given style from the paragraph
3239 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3240 }
3241 else if (resetExistingStyle)
523d2f14
JS
3242 newPara->GetAttributes() = wholeStyle;
3243 else
59509217 3244 {
523d2f14
JS
3245 if (applyMinimal)
3246 {
3247 // Only apply attributes that will make a difference to the combined
3248 // style as seen on the display
603f702b 3249 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3250 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3251 }
3252 else
3253 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3254 }
59509217 3255 }
5d7836c4 3256
5912d19e 3257 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3258 // since they will computed as needed. Only apply the character styling if it's _only_
3259 // character styling. This policy is subject to change and might be put under user control.
3260
59509217
JS
3261 // Hm. we might well be applying a mix of paragraph and character styles, in which
3262 // case we _do_ want to apply character styles regardless of what para styles are set.
3263 // But if we're applying a paragraph style, which has some character attributes, but
3264 // we only want the paragraphs to hold this character style, then we _don't_ want to
3265 // apply the character style. So we need to be able to choose.
3266
f1d800d9 3267 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3268 {
3269 wxRichTextRange childRange(range);
3270 childRange.LimitTo(newPara->GetRange());
7fe8059f 3271
5d7836c4
JS
3272 // Find the starting position and if necessary split it so
3273 // we can start applying a different style.
3274 // TODO: check that the style actually changes or is different
3275 // from style outside of range
4e09ebe8
JS
3276 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3277 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3278
5d7836c4
JS
3279 if (childRange.GetStart() == newPara->GetRange().GetStart())
3280 firstObject = newPara->GetChildren().GetFirst()->GetData();
3281 else
3282 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3283
5d7836c4
JS
3284 // Increment by 1 because we're apply the style one _after_ the split point
3285 long splitPoint = childRange.GetEnd();
3286 if (splitPoint != newPara->GetRange().GetEnd())
3287 splitPoint ++;
7fe8059f 3288
5d7836c4 3289 // Find last object
4b3483e7 3290 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3291 lastObject = newPara->GetChildren().GetLast()->GetData();
3292 else
3293 // lastObject is set as a side-effect of splitting. It's
3294 // returned as the object before the new object.
3295 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3296
5d7836c4
JS
3297 wxASSERT(firstObject != NULL);
3298 wxASSERT(lastObject != NULL);
7fe8059f 3299
5d7836c4
JS
3300 if (!firstObject || !lastObject)
3301 continue;
7fe8059f 3302
5d7836c4
JS
3303 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3304 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3305
4c9847e1
MW
3306 wxASSERT(firstNode);
3307 wxASSERT(lastNode);
7fe8059f 3308
5d7836c4 3309 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3310
5d7836c4
JS
3311 while (node2)
3312 {
3313 wxRichTextObject* child = node2->GetData();
7fe8059f 3314
aeb6ebe2
JS
3315 if (removeStyle)
3316 {
3317 // Removes the given style from the paragraph
3318 wxRichTextRemoveStyle(child->GetAttributes(), style);
3319 }
3320 else if (resetExistingStyle)
5fe7fce4
JS
3321 {
3322 // Preserve the URL as it's not really a formatting style but a property of the object
3323 wxString url;
3324 if (child->GetAttributes().HasURL() && !characterAttributes.HasURL())
3325 url = child->GetAttributes().GetURL();
3326
523d2f14 3327 child->GetAttributes() = characterAttributes;
5fe7fce4
JS
3328
3329 if (!url.IsEmpty())
3330 child->GetAttributes().SetURL(url);
3331 }
523d2f14 3332 else
59509217 3333 {
523d2f14
JS
3334 if (applyMinimal)
3335 {
3336 // Only apply attributes that will make a difference to the combined
3337 // style as seen on the display
603f702b 3338 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3339 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3340 }
3341 else
3342 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3343 }
59509217 3344
5d7836c4
JS
3345 if (node2 == lastNode)
3346 break;
7fe8059f 3347
5d7836c4
JS
3348 node2 = node2->GetNext();
3349 }
3350 }
3351 }
3352 }
3353
3354 node = node->GetNext();
3355 }
3356
3357 // Do action, or delay it until end of batch.
3358 if (haveControl && withUndo)
603f702b 3359 buffer->SubmitAction(action);
5d7836c4
JS
3360
3361 return true;
3362}
3363
603f702b
JS
3364// Just change the attributes for this single object.
3365void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3366{
603f702b 3367 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3368 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3369 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3370 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3371
cdaed652 3372 wxRichTextAction *action = NULL;
603f702b
JS
3373 wxRichTextAttr newAttr = obj->GetAttributes();
3374 if (resetExistingStyle)
3375 newAttr = textAttr;
3376 else
3377 newAttr.Apply(textAttr);
cdaed652
VZ
3378
3379 if (haveControl && withUndo)
3380 {
603f702b
JS
3381 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3382 action->SetRange(obj->GetRange().FromInternal());
3383 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3384 action->MakeObject(obj);
bec80f4f 3385
603f702b 3386 action->GetAttributes() = newAttr;
cdaed652
VZ
3387 }
3388 else
603f702b 3389 obj->GetAttributes() = newAttr;
cdaed652
VZ
3390
3391 if (haveControl && withUndo)
603f702b 3392 buffer->SubmitAction(action);
cdaed652
VZ
3393}
3394
5d7836c4 3395/// Get the text attributes for this position.
24777478 3396bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3397{
fe5aa22c
JS
3398 return DoGetStyle(position, style, true);
3399}
e191ee87 3400
24777478 3401bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3402{
3403 return DoGetStyle(position, style, false);
3404}
3405
fe5aa22c
JS
3406/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3407/// context attributes.
24777478 3408bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3409{
4e09ebe8 3410 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3411
5d7836c4 3412 if (style.IsParagraphStyle())
fe5aa22c 3413 {
5d7836c4 3414 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3415 if (obj)
3416 {
fe5aa22c
JS
3417 if (combineStyles)
3418 {
3419 // Start with the base style
3420 style = GetAttributes();
32423dd8 3421 style.GetTextBoxAttr().Reset();
e191ee87 3422
fe5aa22c
JS
3423 // Apply the paragraph style
3424 wxRichTextApplyStyle(style, obj->GetAttributes());
3425 }
3426 else
3427 style = obj->GetAttributes();
5912d19e 3428
fe5aa22c
JS
3429 return true;
3430 }
5d7836c4
JS
3431 }
3432 else
fe5aa22c
JS
3433 {
3434 obj = GetLeafObjectAtPosition(position);
3435 if (obj)
3436 {
fe5aa22c
JS
3437 if (combineStyles)
3438 {
3439 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3440 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3441 }
3442 else
3443 style = obj->GetAttributes();
5912d19e 3444
fe5aa22c
JS
3445 return true;
3446 }
fe5aa22c
JS
3447 }
3448 return false;
5d7836c4
JS
3449}
3450
59509217
JS
3451static bool wxHasStyle(long flags, long style)
3452{
3453 return (flags & style) != 0;
3454}
3455
3456/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3457/// content.
24777478
JS
3458bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3459{
3460 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3461
3462 return true;
3463}
3464
3465/// Get the combined style for a range - if any attribute is different within the range,
3466/// that attribute is not present within the flags.
3467/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3468/// nested.
3469bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3470{
24777478
JS
3471 style = wxRichTextAttr();
3472
c4168888 3473 wxRichTextAttr clashingAttrPara, clashingAttrChar;
24777478 3474 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3475
24777478
JS
3476 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3477 while (node)
59509217 3478 {
603f702b
JS
3479 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3480 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3481 {
24777478 3482 if (para->GetChildren().GetCount() == 0)
59509217 3483 {
603f702b 3484 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3485
c4168888 3486 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
59509217
JS
3487 }
3488 else
3489 {
24777478
JS
3490 wxRichTextRange paraRange(para->GetRange());
3491 paraRange.LimitTo(range);
59509217 3492
24777478
JS
3493 // First collect paragraph attributes only
3494 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3495 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
c4168888 3496 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
9c4cb611 3497
24777478
JS
3498 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3499
3500 while (childNode)
59509217 3501 {
24777478
JS
3502 wxRichTextObject* child = childNode->GetData();
3503 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3504 {
603f702b 3505 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3506
24777478
JS
3507 // Now collect character attributes only
3508 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3509
c4168888 3510 CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
24777478 3511 }
59509217 3512
24777478 3513 childNode = childNode->GetNext();
59509217
JS
3514 }
3515 }
59509217 3516 }
24777478 3517 node = node->GetNext();
59509217 3518 }
24777478
JS
3519 return true;
3520}
59509217 3521
24777478
JS
3522/// Set default style
3523bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3524{
3525 m_defaultAttributes = style;
3526 return true;
3527}
59509217 3528
24777478
JS
3529/// Test if this whole range has character attributes of the specified kind. If any
3530/// of the attributes are different within the range, the test fails. You
3531/// can use this to implement, for example, bold button updating. style must have
3532/// flags indicating which attributes are of interest.
3533bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3534{
3535 int foundCount = 0;
3536 int matchingCount = 0;
59509217 3537
24777478
JS
3538 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3539 while (node)
59509217 3540 {
24777478 3541 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3542 // wxASSERT (para != NULL);
59509217 3543
24777478 3544 if (para)
59509217 3545 {
24777478
JS
3546 // Stop searching if we're beyond the range of interest
3547 if (para->GetRange().GetStart() > range.GetEnd())
3548 return foundCount == matchingCount && foundCount != 0;
59509217 3549
24777478 3550 if (!para->GetRange().IsOutside(range))
59509217 3551 {
24777478 3552 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3553
24777478
JS
3554 while (node2)
3555 {
3556 wxRichTextObject* child = node2->GetData();
3557 // Allow for empty string if no buffer
3558 wxRichTextRange childRange = child->GetRange();
3559 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3560 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3561
345c78ca 3562 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
24777478
JS
3563 {
3564 foundCount ++;
3565 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3566
32423dd8 3567 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
24777478
JS
3568 matchingCount ++;
3569 }
59509217 3570
24777478
JS
3571 node2 = node2->GetNext();
3572 }
59509217
JS
3573 }
3574 }
59509217 3575
24777478 3576 node = node->GetNext();
59509217
JS
3577 }
3578
24777478
JS
3579 return foundCount == matchingCount && foundCount != 0;
3580}
59509217 3581
24777478
JS
3582/// Test if this whole range has paragraph attributes of the specified kind. If any
3583/// of the attributes are different within the range, the test fails. You
3584/// can use this to implement, for example, centering button updating. style must have
3585/// flags indicating which attributes are of interest.
3586bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3587{
3588 int foundCount = 0;
3589 int matchingCount = 0;
3590
3591 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3592 while (node)
38f833b1 3593 {
24777478 3594 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3595 // wxASSERT (para != NULL);
24777478
JS
3596
3597 if (para)
38f833b1 3598 {
24777478
JS
3599 // Stop searching if we're beyond the range of interest
3600 if (para->GetRange().GetStart() > range.GetEnd())
3601 return foundCount == matchingCount && foundCount != 0;
3602
3603 if (!para->GetRange().IsOutside(range))
38f833b1 3604 {
24777478
JS
3605 wxRichTextAttr textAttr = GetAttributes();
3606 // Apply the paragraph style
3607 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3608
3609 foundCount ++;
32423dd8 3610 if (textAttr.EqPartial(style, false /* strong test */))
24777478 3611 matchingCount ++;
38f833b1
JS
3612 }
3613 }
24777478
JS
3614
3615 node = node->GetNext();
38f833b1 3616 }
24777478
JS
3617 return foundCount == matchingCount && foundCount != 0;
3618}
5d7836c4 3619
cc2aecde
JS
3620void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3621{
3622 wxRichTextBuffer* buffer = GetBuffer();
3623 if (buffer && buffer->GetRichTextCtrl())
3624 buffer->GetRichTextCtrl()->PrepareContent(container);
3625}
3626
590a0f8b
JS
3627/// Set character or paragraph properties
3628bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3629{
3630 wxRichTextBuffer* buffer = GetBuffer();
3631
3632 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3633 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3634 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3635 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3636 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3637
3638 // If we are associated with a control, make undoable; otherwise, apply immediately
3639 // to the data.
3640
3641 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3642
3643 wxRichTextAction* action = NULL;
3644
3645 if (haveControl && withUndo)
3646 {
3647 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3648 action->SetRange(range);
3649 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3650 }
3651
3652 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3653 while (node)
3654 {
3655 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3656 // wxASSERT (para != NULL);
3657
3658 if (para && para->GetChildCount() > 0)
3659 {
3660 // Stop searching if we're beyond the range of interest
3661 if (para->GetRange().GetStart() > range.GetEnd())
3662 break;
3663
3664 if (!para->GetRange().IsOutside(range))
3665 {
3666 // We'll be using a copy of the paragraph to make style changes,
3667 // not updating the buffer directly.
3668 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3669
3670 if (haveControl && withUndo)
3671 {
3672 newPara = new wxRichTextParagraph(*para);
3673 action->GetNewParagraphs().AppendChild(newPara);
3674
3675 // Also store the old ones for Undo
3676 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3677 }
3678 else
3679 newPara = para;
3680
3681 if (parasOnly)
3682 {
3683 if (removeProperties)
3684 {
3685 // Removes the given style from the paragraph
3686 // TODO
3687 newPara->GetProperties().RemoveProperties(properties);
3688 }
3689 else if (resetExistingProperties)
3690 newPara->GetProperties() = properties;
3691 else
3692 newPara->GetProperties().MergeProperties(properties);
3693 }
3694
3695 // When applying paragraph styles dynamically, don't change the text objects' attributes
3696 // since they will computed as needed. Only apply the character styling if it's _only_
3697 // character styling. This policy is subject to change and might be put under user control.
3698
3699 // Hm. we might well be applying a mix of paragraph and character styles, in which
3700 // case we _do_ want to apply character styles regardless of what para styles are set.
3701 // But if we're applying a paragraph style, which has some character attributes, but
3702 // we only want the paragraphs to hold this character style, then we _don't_ want to
3703 // apply the character style. So we need to be able to choose.
3704
3705 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3706 {
3707 wxRichTextRange childRange(range);
3708 childRange.LimitTo(newPara->GetRange());
3709
3710 // Find the starting position and if necessary split it so
3711 // we can start applying different properties.
3712 // TODO: check that the properties actually change or are different
3713 // from properties outside of range
3714 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3715 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3716
3717 if (childRange.GetStart() == newPara->GetRange().GetStart())
3718 firstObject = newPara->GetChildren().GetFirst()->GetData();
3719 else
3720 firstObject = newPara->SplitAt(range.GetStart());
3721
3722 // Increment by 1 because we're apply the style one _after_ the split point
3723 long splitPoint = childRange.GetEnd();
3724 if (splitPoint != newPara->GetRange().GetEnd())
3725 splitPoint ++;
3726
3727 // Find last object
3728 if (splitPoint == newPara->GetRange().GetEnd())
3729 lastObject = newPara->GetChildren().GetLast()->GetData();
3730 else
3731 // lastObject is set as a side-effect of splitting. It's
3732 // returned as the object before the new object.
3733 (void) newPara->SplitAt(splitPoint, & lastObject);
3734
3735 wxASSERT(firstObject != NULL);
3736 wxASSERT(lastObject != NULL);
3737
3738 if (!firstObject || !lastObject)
3739 continue;
3740
3741 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3742 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3743
3744 wxASSERT(firstNode);
3745 wxASSERT(lastNode);
3746
3747 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3748
3749 while (node2)
3750 {
3751 wxRichTextObject* child = node2->GetData();
3752
3753 if (removeProperties)
3754 {
3755 // Removes the given properties from the paragraph
3756 child->GetProperties().RemoveProperties(properties);
3757 }
3758 else if (resetExistingProperties)
3759 child->GetProperties() = properties;
3760 else
3761 {
3762 child->GetProperties().MergeProperties(properties);
3763 }
3764
3765 if (node2 == lastNode)
3766 break;
3767
3768 node2 = node2->GetNext();
3769 }
3770 }
3771 }
3772 }
3773
3774 node = node->GetNext();
3775 }
3776
3777 // Do action, or delay it until end of batch.
3778 if (haveControl && withUndo)
3779 buffer->SubmitAction(action);
3780
3781 return true;
3782}
3783
5d7836c4
JS
3784void wxRichTextParagraphLayoutBox::Reset()
3785{
3786 Clear();
3787
603f702b
JS
3788 wxRichTextBuffer* buffer = GetBuffer();
3789 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3790 {
ce7fe42e 3791 wxRichTextEvent event(wxEVT_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
603f702b
JS
3792 event.SetEventObject(buffer->GetRichTextCtrl());
3793 event.SetContainer(this);
cd8ba0d9
JS
3794
3795 buffer->SendEvent(event, true);
3796 }
3797
7fe8059f 3798 AddParagraph(wxEmptyString);
3e541562 3799
cc2aecde
JS
3800 PrepareContent(*this);
3801
603f702b 3802 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3803}
3804
38113684
JS
3805/// Invalidate the buffer. With no argument, invalidates whole buffer.
3806void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3807{
603f702b 3808 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3809
603f702b
JS
3810 DoInvalidate(invalidRange);
3811}
3812
3813// Do the (in)validation for this object only
3814void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3815{
1e967276 3816 if (invalidRange == wxRICHTEXT_ALL)
38113684 3817 {
1e967276 3818 m_invalidRange = wxRICHTEXT_ALL;
38113684 3819 }
1e967276 3820 // Already invalidating everything
603f702b
JS
3821 else if (m_invalidRange == wxRICHTEXT_ALL)
3822 {
3823 }
3824 else
3825 {
3826 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3827 m_invalidRange.SetStart(invalidRange.GetStart());
3828 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3829 m_invalidRange.SetEnd(invalidRange.GetEnd());
3830 }
3831}
39a1c2f2 3832
603f702b
JS
3833// Do the (in)validation both up and down the hierarchy
3834void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3835{
3836 Invalidate(invalidRange);
3837
3838 if (invalidRange != wxRICHTEXT_NONE)
3839 {
3840 // Now go up the hierarchy
3841 wxRichTextObject* thisObj = this;
3842 wxRichTextObject* p = GetParent();
3843 while (p)
3844 {
3845 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3846 if (l)
3847 l->DoInvalidate(thisObj->GetRange());
3848
3849 thisObj = p;
3850 p = p->GetParent();
3851 }
3852 }
38113684
JS
3853}
3854
3855/// Get invalid range, rounding to entire paragraphs if argument is true.
3856wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3857{
1e967276 3858 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3859 return m_invalidRange;
39a1c2f2 3860
38113684 3861 wxRichTextRange range = m_invalidRange;
39a1c2f2 3862
38113684
JS
3863 if (wholeParagraphs)
3864 {
3865 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3866 if (para1)
3867 range.SetStart(para1->GetRange().GetStart());
f7667b84
JS
3868
3869 // FIXME: be more intelligent about this. Check if we have floating objects
3870 // before the end of the range. But it's not clear how we can in general
3871 // tell where it's safe to stop laying out.
3872 // Anyway, this code is central to efficiency when laying in floating mode.
3873 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3874 {
3875 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
3876 if (para2)
3877 range.SetEnd(para2->GetRange().GetEnd());
3878 }
3879 else
3880 // Floating layout means that all children should be laid out,
3881 // because we can't tell how the whole buffer will be affected.
3882 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3883 }
3884 return range;
3885}
3886
fe5aa22c
JS
3887/// Apply the style sheet to the buffer, for example if the styles have changed.
3888bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3889{
3890 wxASSERT(styleSheet != NULL);
3891 if (!styleSheet)
3892 return false;
3893
3894 int foundCount = 0;
3895
44580804
JS
3896 wxRichTextAttr attr(GetBasicStyle());
3897 if (GetBasicStyle().HasParagraphStyleName())
3898 {
3899 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3900 if (paraDef)
3901 {
3902 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3903 SetBasicStyle(attr);
3904 foundCount ++;
3905 }
3906 }
3907
3908 if (GetBasicStyle().HasCharacterStyleName())
3909 {
3910 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3911 if (charDef)
3912 {
3913 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3914 SetBasicStyle(attr);
3915 foundCount ++;
3916 }
3917 }
3918
fe5aa22c
JS
3919 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3920 while (node)
3921 {
3922 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3923 // wxASSERT (para != NULL);
fe5aa22c
JS
3924
3925 if (para)
3926 {
38f833b1
JS
3927 // Combine paragraph and list styles. If there is a list style in the original attributes,
3928 // the current indentation overrides anything else and is used to find the item indentation.
3929 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3930 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3931 // exception as above).
3932 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3933 // So when changing a list style interactively, could retrieve level based on current style, then
3934 // set appropriate indent and apply new style.
41a85215 3935
bbd55ff9
JS
3936 int outline = -1;
3937 int num = -1;
3938 if (para->GetAttributes().HasOutlineLevel())
3939 outline = para->GetAttributes().GetOutlineLevel();
3940 if (para->GetAttributes().HasBulletNumber())
3941 num = para->GetAttributes().GetBulletNumber();
3942
38f833b1
JS
3943 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3944 {
3945 int currentIndent = para->GetAttributes().GetLeftIndent();
3946
3947 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3948 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3949 if (paraDef && !listDef)
3950 {
336d8ae9 3951 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3952 foundCount ++;
3953 }
3954 else if (listDef && !paraDef)
3955 {
3956 // Set overall style defined for the list style definition
336d8ae9 3957 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3958
3959 // Apply the style for this level
3960 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3961 foundCount ++;
3962 }
3963 else if (listDef && paraDef)
3964 {
3965 // Combines overall list style, style for level, and paragraph style
336d8ae9 3966 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3967 foundCount ++;
3968 }
3969 }
3970 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3971 {
3972 int currentIndent = para->GetAttributes().GetLeftIndent();
3973
3974 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3975
41a85215 3976 // Overall list definition style
336d8ae9 3977 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3978
38f833b1
JS
3979 // Style for this level
3980 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3981
3982 foundCount ++;
3983 }
3984 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3985 {
3986 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3987 if (def)
3988 {
336d8ae9 3989 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3990 foundCount ++;
3991 }
3992 }
bbd55ff9
JS
3993
3994 if (outline != -1)
3995 para->GetAttributes().SetOutlineLevel(outline);
3996 if (num != -1)
3997 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3998 }
3999
4000 node = node->GetNext();
4001 }
4002 return foundCount != 0;
4003}
4004
38f833b1
JS
4005/// Set list style
4006bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4007{
603f702b
JS
4008 wxRichTextBuffer* buffer = GetBuffer();
4009 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 4010
38f833b1
JS
4011 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4012 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4013 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4014 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4015
38f833b1
JS
4016 // Current number, if numbering
4017 int n = startFrom;
41a85215 4018
38f833b1
JS
4019 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4020
4021 // If we are associated with a control, make undoable; otherwise, apply immediately
4022 // to the data.
4023
603f702b 4024 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4025
4026 wxRichTextAction* action = NULL;
4027
4028 if (haveControl && withUndo)
4029 {
603f702b 4030 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4031 action->SetRange(range);
603f702b 4032 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4033 }
4034
4035 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4036 while (node)
4037 {
4038 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4039 // wxASSERT (para != NULL);
38f833b1
JS
4040
4041 if (para && para->GetChildCount() > 0)
4042 {
4043 // Stop searching if we're beyond the range of interest
4044 if (para->GetRange().GetStart() > range.GetEnd())
4045 break;
4046
4047 if (!para->GetRange().IsOutside(range))
4048 {
4049 // We'll be using a copy of the paragraph to make style changes,
4050 // not updating the buffer directly.
4051 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4052
4053 if (haveControl && withUndo)
4054 {
4055 newPara = new wxRichTextParagraph(*para);
4056 action->GetNewParagraphs().AppendChild(newPara);
4057
4058 // Also store the old ones for Undo
4059 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4060 }
4061 else
4062 newPara = para;
41a85215 4063
38f833b1
JS
4064 if (def)
4065 {
4066 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4067 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 4068
38f833b1
JS
4069 // How is numbering going to work?
4070 // If we are renumbering, or numbering for the first time, we need to keep
4071 // track of the number for each level. But we might be simply applying a different
4072 // list style.
4073 // In Word, applying a style to several paragraphs, even if at different levels,
4074 // reverts the level back to the same one. So we could do the same here.
4075 // Renumbering will need to be done when we promote/demote a paragraph.
4076
4077 // Apply the overall list style, and item style for this level
24777478 4078 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4079 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4080
d2d0adc7 4081 // Now we need to do numbering
4ce3ebd3
JS
4082 // Preserve the existing list item continuation bullet style, if any
4083 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4084 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4085 else
38f833b1 4086 {
4ce3ebd3
JS
4087 if (renumber)
4088 {
4089 newPara->GetAttributes().SetBulletNumber(n);
4090 }
41a85215 4091
4ce3ebd3
JS
4092 n ++;
4093 }
38f833b1
JS
4094 }
4095 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4096 {
4097 // if def is NULL, remove list style, applying any associated paragraph style
4098 // to restore the attributes
4099
4100 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4101 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 4102 newPara->GetAttributes().SetBulletText(wxEmptyString);
c4168888 4103 newPara->GetAttributes().SetBulletStyle(0);
41a85215 4104
38f833b1 4105 // Eliminate the main list-related attributes
d2d0adc7 4106 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 4107
38f833b1
JS
4108 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4109 {
4110 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4111 if (def)
4112 {
336d8ae9 4113 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
4114 }
4115 }
4116 }
4117 }
4118 }
4119
4120 node = node->GetNext();
4121 }
4122
4123 // Do action, or delay it until end of batch.
4124 if (haveControl && withUndo)
603f702b 4125 buffer->SubmitAction(action);
38f833b1
JS
4126
4127 return true;
4128}
4129
4130bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4131{
603f702b
JS
4132 wxRichTextBuffer* buffer = GetBuffer();
4133 if (buffer && buffer->GetStyleSheet())
38f833b1 4134 {
603f702b 4135 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4136 if (def)
4137 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4138 }
4139 return false;
4140}
4141
4142/// Clear list for given range
4143bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4144{
4145 return SetListStyle(range, NULL, flags);
4146}
4147
4148/// Number/renumber any list elements in the given range
4149bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4150{
4151 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4152}
4153
4154/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4155bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4156 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4157{
603f702b
JS
4158 wxRichTextBuffer* buffer = GetBuffer();
4159 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4160
38f833b1
JS
4161 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4162 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 4163#if wxDEBUG_LEVEL
38f833b1 4164 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 4165#endif
38f833b1
JS
4166
4167 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4168
38f833b1
JS
4169 // Max number of levels
4170 const int maxLevels = 10;
41a85215 4171
38f833b1
JS
4172 // The level we're looking at now
4173 int currentLevel = -1;
41a85215 4174
38f833b1
JS
4175 // The item number for each level
4176 int levels[maxLevels];
4177 int i;
41a85215 4178
38f833b1
JS
4179 // Reset all numbering
4180 for (i = 0; i < maxLevels; i++)
4181 {
4182 if (startFrom != -1)
d2d0adc7 4183 levels[i] = startFrom-1;
38f833b1 4184 else if (renumber) // start again
d2d0adc7 4185 levels[i] = 0;
38f833b1
JS
4186 else
4187 levels[i] = -1; // start from the number we found, if any
4188 }
41a85215 4189
bb7bbd12 4190#if wxDEBUG_LEVEL
38f833b1 4191 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
bb7bbd12 4192#endif
38f833b1
JS
4193
4194 // If we are associated with a control, make undoable; otherwise, apply immediately
4195 // to the data.
4196
603f702b 4197 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4198
4199 wxRichTextAction* action = NULL;
4200
4201 if (haveControl && withUndo)
4202 {
603f702b 4203 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4204 action->SetRange(range);
603f702b 4205 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4206 }
4207
4208 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4209 while (node)
4210 {
4211 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4212 // wxASSERT (para != NULL);
38f833b1
JS
4213
4214 if (para && para->GetChildCount() > 0)
4215 {
4216 // Stop searching if we're beyond the range of interest
4217 if (para->GetRange().GetStart() > range.GetEnd())
4218 break;
4219
4220 if (!para->GetRange().IsOutside(range))
4221 {
4222 // We'll be using a copy of the paragraph to make style changes,
4223 // not updating the buffer directly.
4224 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4225
4226 if (haveControl && withUndo)
4227 {
4228 newPara = new wxRichTextParagraph(*para);
4229 action->GetNewParagraphs().AppendChild(newPara);
4230
4231 // Also store the old ones for Undo
4232 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4233 }
4234 else
4235 newPara = para;
41a85215 4236
38f833b1
JS
4237 wxRichTextListStyleDefinition* defToUse = def;
4238 if (!defToUse)
4239 {
336d8ae9
VZ
4240 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4241 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 4242 }
41a85215 4243
38f833b1
JS
4244 if (defToUse)
4245 {
4246 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4247 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4248
d2d0adc7
JS
4249 // If we've specified a level to apply to all, change the level.
4250 if (specifiedLevel != -1)
38f833b1 4251 thisLevel = specifiedLevel;
41a85215 4252
38f833b1
JS
4253 // Do promotion if specified
4254 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4255 {
4256 thisLevel = thisLevel - promoteBy;
4257 if (thisLevel < 0)
4258 thisLevel = 0;
4259 if (thisLevel > 9)
4260 thisLevel = 9;
4261 }
41a85215 4262
38f833b1 4263 // Apply the overall list style, and item style for this level
24777478 4264 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4265 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4266
4ce3ebd3
JS
4267 // Preserve the existing list item continuation bullet style, if any
4268 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4269 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4270
38f833b1 4271 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 4272
38f833b1
JS
4273 if (currentLevel == -1)
4274 currentLevel = thisLevel;
41a85215 4275
38f833b1
JS
4276 // Same level as before, do nothing except increment level's number afterwards
4277 if (currentLevel == thisLevel)
4278 {
4279 }
4280 // A deeper level: start renumbering all levels after current level
4281 else if (thisLevel > currentLevel)
4282 {
4283 for (i = currentLevel+1; i <= thisLevel; i++)
4284 {
d2d0adc7 4285 levels[i] = 0;
38f833b1
JS
4286 }
4287 currentLevel = thisLevel;
4288 }
4289 else if (thisLevel < currentLevel)
4290 {
4291 currentLevel = thisLevel;
41a85215 4292 }
38f833b1
JS
4293
4294 // Use the current numbering if -1 and we have a bullet number already
4295 if (levels[currentLevel] == -1)
4296 {
4297 if (newPara->GetAttributes().HasBulletNumber())
4298 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4299 else
4300 levels[currentLevel] = 1;
4301 }
d2d0adc7
JS
4302 else
4303 {
4ce3ebd3
JS
4304 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4305 levels[currentLevel] ++;
d2d0adc7 4306 }
41a85215 4307
38f833b1
JS
4308 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4309
d2d0adc7
JS
4310 // Create the bullet text if an outline list
4311 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4312 {
4313 wxString text;
4314 for (i = 0; i <= currentLevel; i++)
4315 {
4316 if (!text.IsEmpty())
4317 text += wxT(".");
4318 text += wxString::Format(wxT("%d"), levels[i]);
4319 }
4320 newPara->GetAttributes().SetBulletText(text);
4321 }
38f833b1
JS
4322 }
4323 }
4324 }
4325
4326 node = node->GetNext();
4327 }
4328
4329 // Do action, or delay it until end of batch.
4330 if (haveControl && withUndo)
603f702b 4331 buffer->SubmitAction(action);
38f833b1
JS
4332
4333 return true;
4334}
4335
4336bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4337{
603f702b
JS
4338 wxRichTextBuffer* buffer = GetBuffer();
4339 if (buffer->GetStyleSheet())
38f833b1
JS
4340 {
4341 wxRichTextListStyleDefinition* def = NULL;
4342 if (!defName.IsEmpty())
603f702b 4343 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4344 return NumberList(range, def, flags, startFrom, specifiedLevel);
4345 }
4346 return false;
4347}
4348
4349/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4350bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4351{
4352 // TODO
4353 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4354 // to NumberList with a flag indicating promotion is required within one of the ranges.
4355 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4356 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4357 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4358 // list position will start from 1.
4359 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4360 // We can end the renumbering at this point.
41a85215 4361
38f833b1 4362 // For now, only renumber within the promotion range.
41a85215 4363
38f833b1
JS
4364 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4365}
4366
4367bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4368{
603f702b
JS
4369 wxRichTextBuffer* buffer = GetBuffer();
4370 if (buffer->GetStyleSheet())
38f833b1
JS
4371 {
4372 wxRichTextListStyleDefinition* def = NULL;
4373 if (!defName.IsEmpty())
603f702b 4374 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4375 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4376 }
4377 return false;
4378}
4379
d2d0adc7
JS
4380/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4381/// position of the paragraph that it had to start looking from.
24777478 4382bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 4383{
c4168888 4384 // TODO: add GetNextChild/GetPreviousChild to composite
4ce3ebd3
JS
4385 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4386 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4387 {
4388 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4389 if (node)
4390 {
4391 node = node->GetPrevious();
4392 if (node)
4393 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4394 else
4395 previousParagraph = NULL;
4396 }
4397 else
4398 previousParagraph = NULL;
4399 }
4400
c4168888 4401 if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
d2d0adc7 4402 return false;
3e541562 4403
603f702b
JS
4404 wxRichTextBuffer* buffer = GetBuffer();
4405 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4406 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 4407 {
336d8ae9 4408 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
4409 if (def)
4410 {
4411 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4412 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4413
d2d0adc7
JS
4414 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4415
4416 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4417 if (previousParagraph->GetAttributes().HasBulletName())
4418 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4419 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4420 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4421
d2d0adc7
JS
4422 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4423 attr.SetBulletNumber(nextNumber);
3e541562 4424
d2d0adc7
JS
4425 if (isOutline)
4426 {
4427 wxString text = previousParagraph->GetAttributes().GetBulletText();
4428 if (!text.IsEmpty())
4429 {
4430 int pos = text.Find(wxT('.'), true);
4431 if (pos != wxNOT_FOUND)
4432 {
4433 text = text.Mid(0, text.Length() - pos - 1);
4434 }
4435 else
4436 text = wxEmptyString;
4437 if (!text.IsEmpty())
4438 text += wxT(".");
4439 text += wxString::Format(wxT("%d"), nextNumber);
4440 attr.SetBulletText(text);
4441 }
4442 }
3e541562 4443
d2d0adc7
JS
4444 return true;
4445 }
4446 else
4447 return false;
4448 }
4449 else
4450 return false;
4451}
4452
5d7836c4
JS
4453/*!
4454 * wxRichTextParagraph
4455 * This object represents a single paragraph (or in a straight text editor, a line).
4456 */
4457
603f702b 4458IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4459
cfa3b256
JS
4460wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4461
24777478 4462wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4463 wxRichTextCompositeObject(parent)
5d7836c4 4464{
5d7836c4
JS
4465 if (style)
4466 SetAttributes(*style);
4467}
4468
24777478 4469wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4470 wxRichTextCompositeObject(parent)
5d7836c4 4471{
4f32b3cf
JS
4472 if (paraStyle)
4473 SetAttributes(*paraStyle);
5d7836c4 4474
4f32b3cf 4475 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4476}
4477
4478wxRichTextParagraph::~wxRichTextParagraph()
4479{
4480 ClearLines();
4481}
4482
4483/// Draw the item
8db2e3ef 4484bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4485{
603f702b
JS
4486 if (!IsShown())
4487 return true;
4488
4489 // Currently we don't merge these attributes with the parent, but we
4490 // should consider whether we should (e.g. if we set a border colour
4491 // for all paragraphs). But generally box attributes are likely to be
4492 // different for different objects.
4493 wxRect paraRect = GetRect();
24777478 4494 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef
JS
4495 context.ApplyVirtualAttributes(attr, this);
4496
4497 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
fe5aa22c 4498
5d7836c4 4499 // Draw the bullet, if any
4ce3ebd3 4500 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
5d7836c4 4501 {
fe5aa22c 4502 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4503 {
fe5aa22c 4504 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4505 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4506
8db2e3ef 4507 wxRichTextAttr bulletAttr(attr);
d2d0adc7 4508
e3eac0ff
JS
4509 // Combine with the font of the first piece of content, if one is specified
4510 if (GetChildren().GetCount() > 0)
4511 {
4512 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4513 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4514 {
4515 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4516 }
4517 }
4518
d2d0adc7 4519 // Get line height from first line, if any
d3b9f782 4520 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4521
4522 wxPoint linePos;
4523 int lineHeight wxDUMMY_INITIALIZE(0);
4524 if (line)
5d7836c4 4525 {
d2d0adc7
JS
4526 lineHeight = line->GetSize().y;
4527 linePos = line->GetPosition() + GetPosition();
5d7836c4 4528 }
d2d0adc7 4529 else
f089713f 4530 {
f089713f 4531 wxFont font;
44cc96a8
JS
4532 if (bulletAttr.HasFont() && GetBuffer())
4533 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4534 else
4535 font = (*wxNORMAL_FONT);
4536
ecb5fbf1 4537 wxCheckSetFont(dc, font);
f089713f 4538
d2d0adc7
JS
4539 lineHeight = dc.GetCharHeight();
4540 linePos = GetPosition();
4541 linePos.y += spaceBeforePara;
4542 }
f089713f 4543
d2d0adc7 4544 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4545
d2d0adc7
JS
4546 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4547 {
4548 if (wxRichTextBuffer::GetRenderer())
4549 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4550 }
3e541562
JS
4551 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4552 {
d2d0adc7
JS
4553 if (wxRichTextBuffer::GetRenderer())
4554 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4555 }
5d7836c4
JS
4556 else
4557 {
4558 wxString bulletText = GetBulletText();
3e541562 4559
d2d0adc7
JS
4560 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4561 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4562 }
4563 }
4564 }
7fe8059f 4565
5d7836c4
JS
4566 // Draw the range for each line, one object at a time.
4567
4568 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4569 while (node)
4570 {
4571 wxRichTextLine* line = node->GetData();
1e967276 4572 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4573
5d7836c4
JS
4574 // Lines are specified relative to the paragraph
4575
4576 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4577
7051fa41
JS
4578 // Don't draw if off the screen
4579 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4580 {
7051fa41
JS
4581 wxPoint objectPosition = linePosition;
4582 int maxDescent = line->GetDescent();
4583
4584 // Loop through objects until we get to the one within range
4585 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4586
7051fa41
JS
4587 int i = 0;
4588 while (node2)
5d7836c4 4589 {
7051fa41 4590 wxRichTextObject* child = node2->GetData();
5d7836c4 4591
e12b91a3 4592 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4593 {
7051fa41
JS
4594 // Draw this part of the line at the correct position
4595 wxRichTextRange objectRange(child->GetRange());
4596 objectRange.LimitTo(lineRange);
4597
4598 wxSize objectSize;
603f702b 4599 if (child->IsTopLevel())
7051fa41 4600 {
603f702b
JS
4601 objectSize = child->GetCachedSize();
4602 objectRange = child->GetOwnRange();
7051fa41
JS
4603 }
4604 else
7051fa41 4605 {
603f702b
JS
4606#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4607 if (i < (int) line->GetObjectSizes().GetCount())
4608 {
4609 objectSize.x = line->GetObjectSizes()[(size_t) i];
4610 }
4611 else
4612#endif
4613 {
4614 int descent = 0;
8db2e3ef 4615 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
603f702b 4616 }
7051fa41 4617 }
5d7836c4 4618
7051fa41
JS
4619 // Use the child object's width, but the whole line's height
4620 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
8db2e3ef 4621 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4622
7051fa41
JS
4623 objectPosition.x += objectSize.x;
4624 i ++;
4625 }
4626 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4627 // Can break out of inner loop now since we've passed this line's range
4628 break;
5d7836c4 4629
7051fa41
JS
4630 node2 = node2->GetNext();
4631 }
5d7836c4
JS
4632 }
4633
4634 node = node->GetNext();
7fe8059f 4635 }
5d7836c4
JS
4636
4637 return true;
4638}
4639
4f3d5bc0
JS
4640// Get the range width using partial extents calculated for the whole paragraph.
4641static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4642{
4643 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4644
affbfa1f
JS
4645 if (partialExtents.GetCount() < (size_t) range.GetLength())
4646 return 0;
4647
4f3d5bc0
JS
4648 int leftMostPos = 0;
4649 if (range.GetStart() - para.GetRange().GetStart() > 0)
4650 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4651
4652 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4653
4654 int w = rightMostPos - leftMostPos;
4655
4656 return w;
4657}
4658
5d7836c4 4659/// Lay the item out
8db2e3ef 4660bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 4661{
cdaed652
VZ
4662 // Deal with floating objects firstly before the normal layout
4663 wxRichTextBuffer* buffer = GetBuffer();
4664 wxASSERT(buffer);
e12b91a3 4665
07d4142f 4666 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
e12b91a3
JS
4667
4668 if (wxRichTextBuffer::GetFloatingLayoutMode())
4669 {
4670 wxASSERT(collector != NULL);
4671 if (collector)
4672 LayoutFloat(dc, context, rect, parentRect, style, collector);
4673 }
cdaed652 4674
24777478 4675 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef 4676 context.ApplyVirtualAttributes(attr, this);
fe5aa22c 4677
169adfa9
JS
4678 // ClearLines();
4679
5d7836c4 4680 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4681 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4682 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4683 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4684 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4685 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4686
4687 int lineSpacing = 0;
4688
4689 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
77120d82 4690 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.HasFont())
5d7836c4 4691 {
77120d82
JS
4692 wxFont font(buffer->GetFontTable().FindFont(attr));
4693 if (font.IsOk())
4694 {
4695 wxCheckSetFont(dc, font);
4696 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4697 }
5d7836c4
JS
4698 }
4699
5d7836c4
JS
4700 // Start position for each line relative to the paragraph
4701 int startPositionFirstLine = leftIndent;
4702 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4703
4704 // If we have a bullet in this paragraph, the start position for the first line's text
4705 // is actually leftIndent + leftSubIndent.
fe5aa22c 4706 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4707 startPositionFirstLine = startPositionSubsequentLines;
4708
5d7836c4
JS
4709 long lastEndPos = GetRange().GetStart()-1;
4710 long lastCompletedEndPos = lastEndPos;
4711
4712 int currentWidth = 0;
4713 SetPosition(rect.GetPosition());
4714
4715 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4716 int lineHeight = 0;
4717 int maxWidth = 0;
603f702b 4718 int maxHeight = currentPosition.y;
476a319a 4719 int maxAscent = 0;
5d7836c4 4720 int maxDescent = 0;
5d7836c4 4721 int lineCount = 0;
cdaed652
VZ
4722 int lineAscent = 0;
4723 int lineDescent = 0;
5d7836c4 4724
2f45f554
JS
4725 wxRichTextObjectList::compatibility_iterator node;
4726
4727#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4728 wxUnusedVar(style);
4729 wxArrayInt partialExtents;
4730
4731 wxSize paraSize;
8aab23a1 4732 int paraDescent = 0;
2f45f554
JS
4733
4734 // This calculates the partial text extents
914a4e23 4735 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents);
2f45f554
JS
4736#else
4737 node = m_children.GetFirst();
ecb5fbf1
JS
4738 while (node)
4739 {
4740 wxRichTextObject* child = node->GetData();
4741
603f702b 4742 //child->SetCachedSize(wxDefaultSize);
8db2e3ef 4743 child->Layout(dc, context, rect, style);
ecb5fbf1
JS
4744
4745 node = node->GetNext();
4746 }
31778480
JS
4747#endif
4748
5d7836c4
JS
4749 // Split up lines
4750
4751 // We may need to go back to a previous child, in which case create the new line,
4752 // find the child corresponding to the start position of the string, and
4753 // continue.
4754
603f702b
JS
4755 wxRect availableRect;
4756
ecb5fbf1 4757 node = m_children.GetFirst();
5d7836c4
JS
4758 while (node)
4759 {
4760 wxRichTextObject* child = node->GetData();
4761
cdaed652 4762 // If floating, ignore. We already laid out floats.
603f702b
JS
4763 // Also ignore if empty object, except if we haven't got any
4764 // size yet.
e12b91a3
JS
4765 if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4766 || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
603f702b 4767 )
affbfa1f
JS
4768 {
4769 node = node->GetNext();
4770 continue;
4771 }
4772
5d7836c4
JS
4773 // If this is e.g. a composite text box, it will need to be laid out itself.
4774 // But if just a text fragment or image, for example, this will
4775 // do nothing. NB: won't we need to set the position after layout?
4776 // since for example if position is dependent on vertical line size, we
4777 // can't tell the position until the size is determined. So possibly introduce
4778 // another layout phase.
4779
5d7836c4
JS
4780 // We may only be looking at part of a child, if we searched back for wrapping
4781 // and found a suitable point some way into the child. So get the size for the fragment
4782 // if necessary.
3e541562 4783
ff76711f
JS
4784 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4785 long lastPosToUse = child->GetRange().GetEnd();
4786 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4787
ff76711f
JS
4788 if (lineBreakInThisObject)
4789 lastPosToUse = nextBreakPos;
5d7836c4
JS
4790
4791 wxSize childSize;
4792 int childDescent = 0;
3e541562 4793
603f702b
JS
4794 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4795 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4796 rect.width - startOffset - rightIndent, rect.height);
4797
4798 if (child->IsTopLevel())
4799 {
4800 wxSize oldSize = child->GetCachedSize();
4801
4802 child->Invalidate(wxRICHTEXT_ALL);
4803 child->SetPosition(wxPoint(0, 0));
4804
4805 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4806 // lays out the object again using the minimum size
4807 // The position will be determined by its location in its line,
4808 // and not by the child's actual position.
8db2e3ef
JS
4809 child->LayoutToBestSize(dc, context, buffer,
4810 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4811
4812 if (oldSize != child->GetCachedSize())
4813 {
4814 partialExtents.Clear();
4815
4816 // Recalculate the partial text extents since the child object changed size
914a4e23 4817 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
603f702b
JS
4818 }
4819 }
4820
4821 // Problem: we need to layout composites here for which we need the available width,
4822 // but we can't get the available width without using the float collector which
4823 // needs to know the object height.
4824
ff76711f 4825 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4826 {
4827 childSize = child->GetCachedSize();
4828 childDescent = child->GetDescent();
4829 }
4830 else
4f3d5bc0
JS
4831 {
4832#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4833 // Get height only, then the width using the partial extents
914a4e23 4834 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4f3d5bc0
JS
4835 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4836#else
914a4e23 4837 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize());
4f3d5bc0
JS
4838#endif
4839 }
ff76711f 4840
603f702b
JS
4841 bool doLoop = true;
4842 int loopIterations = 0;
4843
4844 // If there are nested objects that need to lay themselves out, we have to do this in a
4845 // loop because the height of the object may well depend on the available width.
4846 // And because of floating object positioning, the available width depends on the
4847 // height of the object and whether it will clash with the floating objects.
4848 // So, we see whether the available width changes due to the presence of floating images.
4849 // If it does, then we'll use the new restricted width to find the object height again.
4850 // If this causes another restriction in the available width, we'll try again, until
4851 // either we lose patience or the available width settles down.
4852 do
4853 {
4854 loopIterations ++;
4855
4856 wxRect oldAvailableRect = availableRect;
4857
4858 // Available width depends on the floating objects and the line height.
7c9fdebe 4859 // Note: the floating objects may be placed vertically along the two sides of
603f702b
JS
4860 // buffer, so we may have different available line widths with different
4861 // [startY, endY]. So, we can't determine how wide the available
4862 // space is until we know the exact line height.
a70eb13e
JS
4863 if (childDescent == 0)
4864 {
4865 lineHeight = wxMax(lineHeight, childSize.y);
4866 lineDescent = maxDescent;
4867 lineAscent = maxAscent;
4868 }
4869 else
4870 {
4871 lineDescent = wxMax(childDescent, maxDescent);
4872 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4873 }
4874 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
603f702b 4875
e12b91a3 4876 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
603f702b 4877 {
e12b91a3
JS
4878 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4879
4880 // Adjust availableRect to the space that is available when taking floating objects into account.
4881
4882 if (floatAvailableRect.x + startOffset > availableRect.x)
4883 {
4884 int newX = floatAvailableRect.x + startOffset;
4885 int newW = availableRect.width - (newX - availableRect.x);
4886 availableRect.x = newX;
4887 availableRect.width = newW;
4888 }
603f702b 4889
e12b91a3
JS
4890 if (floatAvailableRect.width < availableRect.width)
4891 availableRect.width = floatAvailableRect.width;
4892 }
603f702b
JS
4893
4894 currentPosition.x = availableRect.x - rect.x;
4895
4896 if (child->IsTopLevel() && loopIterations <= 20)
4897 {
4898 if (availableRect != oldAvailableRect)
4899 {
4900 wxSize oldSize = child->GetCachedSize();
4901
603f702b
JS
4902 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4903 // lays out the object again using the minimum size
4904 child->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 4905 child->LayoutToBestSize(dc, context, buffer,
914a4e23 4906 attr, child->GetAttributes(), availableRect, parentRect.GetSize(), style);
603f702b
JS
4907 childSize = child->GetCachedSize();
4908 childDescent = child->GetDescent();
603f702b
JS
4909
4910 if (oldSize != child->GetCachedSize())
4911 {
4912 partialExtents.Clear();
4913
4914 // Recalculate the partial text extents since the child object changed size
914a4e23 4915 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
603f702b 4916 }
cdaed652 4917
603f702b
JS
4918 // Go around the loop finding the available rect for the given floating objects
4919 }
4920 else
4921 doLoop = false;
4922 }
4923 else
4924 doLoop = false;
4925 }
4926 while (doLoop);
cdaed652 4927
20d09da5
JS
4928 if (child->IsTopLevel())
4929 {
4930 // We can move it to the correct position at this point
32423dd8
JS
4931 // TODO: probably need to add margin
4932 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
20d09da5
JS
4933 }
4934
ff76711f
JS
4935 // Cases:
4936 // 1) There was a line break BEFORE the natural break
4937 // 2) There was a line break AFTER the natural break
603f702b
JS
4938 // 3) It's the last line
4939 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4940
603f702b
JS
4941 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4942 ||
4943 (childSize.x + currentWidth > availableRect.width)
914a4e23 4944#if 0
603f702b
JS
4945 ||
4946 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
914a4e23 4947#endif
603f702b 4948 )
5d7836c4
JS
4949 {
4950 long wrapPosition = 0;
603f702b
JS
4951 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4952 wrapPosition = child->GetRange().GetEnd();
4953 else
5d7836c4
JS
4954
4955 // Find a place to wrap. This may walk back to previous children,
4956 // for example if a word spans several objects.
cdaed652
VZ
4957 // Note: one object must contains only one wxTextAtrr, so the line height will not
4958 // change inside one object. Thus, we can pass the remain line width to the
4959 // FindWrapPosition function.
8db2e3ef 4960 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4961 {
4962 // If the function failed, just cut it off at the end of this child.
4963 wrapPosition = child->GetRange().GetEnd();
4964 }
4965
4966 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4967 if (wrapPosition <= lastCompletedEndPos)
4968 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4969
603f702b
JS
4970 // Line end position shouldn't be the same as the end, or greater.
4971 if (wrapPosition >= GetRange().GetEnd())
a8a15de6 4972 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
603f702b 4973
5d7836c4 4974 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4975
5d7836c4
JS
4976 // Let's find the actual size of the current line now
4977 wxSize actualSize;
4978 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4979
a70eb13e 4980 childDescent = 0;
4ab8a5e2 4981
4f3d5bc0 4982#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4983 if (!child->IsEmpty())
4984 {
4985 // Get height only, then the width using the partial extents
914a4e23 4986 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
603f702b
JS
4987 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4988 }
4989 else
4f3d5bc0 4990#endif
914a4e23 4991 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
4f3d5bc0 4992
5d7836c4 4993 currentWidth = actualSize.x;
a70eb13e
JS
4994
4995 // The descent for the whole line at this point, is the correct max descent
4996 maxDescent = childDescent;
4997 // Maximum ascent
4998 maxAscent = actualSize.y-childDescent;
4999
5000 // lineHeight is given by the height for the whole line, since it will
5001 // take into account ascend/descend.
5002 lineHeight = actualSize.y;
7fe8059f 5003
07d4142f 5004 if (lineHeight == 0 && buffer)
603f702b 5005 {
07d4142f 5006 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
5007 wxCheckSetFont(dc, font);
5008 lineHeight = dc.GetCharHeight();
5009 }
5010
5011 if (maxDescent == 0)
5012 {
5013 int w, h;
5014 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5015 }
5016
5d7836c4 5017 // Add a new line
1e967276 5018 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 5019
1e967276
JS
5020 // Set relative range so we won't have to change line ranges when paragraphs are moved
5021 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
5022 line->SetPosition(currentPosition);
5023 line->SetSize(wxSize(currentWidth, lineHeight));
5024 line->SetDescent(maxDescent);
5025
603f702b
JS
5026 maxHeight = currentPosition.y + lineHeight;
5027
5d7836c4
JS
5028 // Now move down a line. TODO: add margins, spacing
5029 currentPosition.y += lineHeight;
5030 currentPosition.y += lineSpacing;
5d7836c4 5031 maxDescent = 0;
476a319a 5032 maxAscent = 0;
603f702b
JS
5033 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5034 currentWidth = 0;
7fe8059f 5035
5d7836c4
JS
5036 lineCount ++;
5037
a70eb13e 5038 // TODO: account for zero-length objects
603f702b 5039 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 5040
5d7836c4
JS
5041 lastEndPos = wrapPosition;
5042 lastCompletedEndPos = lastEndPos;
5043
5044 lineHeight = 0;
5045
603f702b
JS
5046 if (wrapPosition < GetRange().GetEnd()-1)
5047 {
5048 // May need to set the node back to a previous one, due to searching back in wrapping
5049 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5050 if (childAfterWrapPosition)
5051 node = m_children.Find(childAfterWrapPosition);
5052 else
5053 node = node->GetNext();
5054 }
5d7836c4
JS
5055 else
5056 node = node->GetNext();
603f702b
JS
5057
5058 // Apply paragraph styles such as alignment to the wrapped line
5059 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
5060 }
5061 else
5062 {
5063 // We still fit, so don't add a line, and keep going
5064 currentWidth += childSize.x;
a70eb13e
JS
5065
5066 if (childDescent == 0)
5067 {
5068 // An object with a zero descend value wants to take up the whole
5069 // height regardless of baseline
5070 lineHeight = wxMax(lineHeight, childSize.y);
5071 }
5072 else
5073 {
5074 maxDescent = wxMax(childDescent, maxDescent);
5075 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5076 }
5077
5078 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5d7836c4 5079
603f702b 5080 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
5081 lastEndPos = child->GetRange().GetEnd();
5082
5083 node = node->GetNext();
5084 }
5085 }
5086
07d4142f 5087 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 5088
914a4e23
JS
5089 // Add the last line - it's the current pos -> last para pos
5090 // Substract -1 because the last position is always the end-paragraph position.
5091 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
5092 {
5093 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
5094
5095 wxRichTextLine* line = AllocateLine(lineCount);
5096
5097 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
5098
5099 // Set relative range so we won't have to change line ranges when paragraphs are moved
5100 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5101
5102 line->SetPosition(currentPosition);
5103
5104 if (lineHeight == 0 && buffer)
5105 {
5106 wxFont font(buffer->GetFontTable().FindFont(attr));
5107 wxCheckSetFont(dc, font);
5108 lineHeight = dc.GetCharHeight();
5109 }
5110
5111 if (maxDescent == 0)
5112 {
5113 int w, h;
5114 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5115 }
5116
5117 line->SetSize(wxSize(currentWidth, lineHeight));
5118 line->SetDescent(maxDescent);
5119 currentPosition.y += lineHeight;
5120 currentPosition.y += lineSpacing;
5121 lineCount ++;
1bdb8131
JS
5122
5123 // Apply paragraph styles such as alignment to the wrapped line
5124 ApplyParagraphStyle(line, attr, availableRect, dc);
914a4e23
JS
5125 }
5126
1e967276
JS
5127 // Remove remaining unused line objects, if any
5128 ClearUnusedLines(lineCount);
5129
603f702b
JS
5130 // We need to add back the margins etc.
5131 {
5132 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5133 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5134 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5135 SetCachedSize(marginRect.GetSize());
5136 }
5137
5138 // The maximum size is the length of the paragraph stretched out into a line.
5139 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5140 // this size. TODO: take into account line breaks.
5141 {
5142 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 5143 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 5144 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5145 SetMaxSize(marginRect.GetSize());
5146 }
5147
5148 // Find the greatest minimum size. Currently we only look at non-text objects,
5149 // which isn't ideal but it would be slow to find the maximum word width to
5150 // use as the minimum.
5151 {
5152 int minWidth = 0;
5153 node = m_children.GetFirst();
5154 while (node)
5155 {
5156 wxRichTextObject* child = node->GetData();
5157
5158 // If floating, ignore. We already laid out floats.
5159 // Also ignore if empty object, except if we haven't got any
5160 // size yet.
e12b91a3 5161 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
603f702b
JS
5162 {
5163 if (child->GetCachedSize().x > minWidth)
5164 minWidth = child->GetMinSize().x;
5165 }
5166 node = node->GetNext();
5167 }
5d7836c4 5168
603f702b
JS
5169 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5170 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5171 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5172 SetMinSize(marginRect.GetSize());
5173 }
5d7836c4 5174
2f45f554
JS
5175#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5176#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5177 // Use the text extents to calculate the size of each fragment in each line
5178 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5179 while (lineNode)
5180 {
5181 wxRichTextLine* line = lineNode->GetData();
5182 wxRichTextRange lineRange = line->GetAbsoluteRange();
5183
5184 // Loop through objects until we get to the one within range
5185 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5186
5187 while (node2)
5188 {
5189 wxRichTextObject* child = node2->GetData();
5190
affbfa1f 5191 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
5192 {
5193 wxRichTextRange rangeToUse = lineRange;
5194 rangeToUse.LimitTo(child->GetRange());
5195
5196 // Find the size of the child from the text extents, and store in an array
5197 // for drawing later
5198 int left = 0;
5199 if (rangeToUse.GetStart() > GetRange().GetStart())
5200 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5201 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5202 int sz = right - left;
5203 line->GetObjectSizes().Add(sz);
5204 }
5205 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5206 // Can break out of inner loop now since we've passed this line's range
5207 break;
5208
5209 node2 = node2->GetNext();
5210 }
5211
5212 lineNode = lineNode->GetNext();
5213 }
5214#endif
5215#endif
5216
5d7836c4
JS
5217 return true;
5218}
5219
603f702b
JS
5220/// Apply paragraph styles, such as centering, to wrapped lines
5221/// TODO: take into account box attributes, possibly
5222void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5223{
5224 if (!attr.HasAlignment())
5225 return;
5226
5227 wxPoint pos = line->GetPosition();
32423dd8 5228 wxPoint originalPos = pos;
603f702b
JS
5229 wxSize size = line->GetSize();
5230
5231 // centering, right-justification
8db2e3ef 5232 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5233 {
5234 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5235 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5236 line->SetPosition(pos);
5237 }
8db2e3ef 5238 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5239 {
5240 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5241 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5242 line->SetPosition(pos);
5243 }
32423dd8
JS
5244
5245 if (pos != originalPos)
5246 {
5247 wxPoint inc = pos - originalPos;
5248
5249 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5250
5251 while (node)
5252 {
5253 wxRichTextObject* child = node->GetData();
5254 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5255 child->Move(child->GetPosition() + inc);
5256
5257 node = node->GetNext();
5258 }
5259 }
603f702b 5260}
5d7836c4
JS
5261
5262/// Insert text at the given position
5263bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5264{
5265 wxRichTextObject* childToUse = NULL;
09f14108 5266 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5267
5268 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5269 while (node)
5270 {
5271 wxRichTextObject* child = node->GetData();
5272 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5273 {
5274 childToUse = child;
5275 nodeToUse = node;
5276 break;
5277 }
5278
5279 node = node->GetNext();
5280 }
5281
5282 if (childToUse)
5283 {
5284 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5285 if (textObject)
5286 {
5287 int posInString = pos - textObject->GetRange().GetStart();
5288
5289 wxString newText = textObject->GetText().Mid(0, posInString) +
5290 text + textObject->GetText().Mid(posInString);
5291 textObject->SetText(newText);
5292
28f92d74 5293 int textLength = text.length();
5d7836c4
JS
5294
5295 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5296 textObject->GetRange().GetEnd() + textLength));
5297
5298 // Increment the end range of subsequent fragments in this paragraph.
5299 // We'll set the paragraph range itself at a higher level.
5300
5301 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5302 while (node)
5303 {
5304 wxRichTextObject* child = node->GetData();
5305 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5306 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5307
5d7836c4
JS
5308 node = node->GetNext();
5309 }
5310
5311 return true;
5312 }
5313 else
5314 {
5315 // TODO: if not a text object, insert at closest position, e.g. in front of it
5316 }
5317 }
5318 else
5319 {
5320 // Add at end.
5321 // Don't pass parent initially to suppress auto-setting of parent range.
5322 // We'll do that at a higher level.
5323 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5324
5325 AppendChild(textObject);
5326 return true;
5327 }
5328
5329 return false;
5330}
5331
5332void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5333{
bec80f4f 5334 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5335}
5336
5337/// Clear the cached lines
5338void wxRichTextParagraph::ClearLines()
5339{
5340 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5341}
5342
5343/// Get/set the object size for the given range. Returns false if the range
5344/// is invalid for this object.
914a4e23 5345bool 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
5346{
5347 if (!range.IsWithin(GetRange()))
5348 return false;
5349
5350 if (flags & wxRICHTEXT_UNFORMATTED)
5351 {
5352 // Just use unformatted data, assume no line breaks
5d7836c4
JS
5353 wxSize sz;
5354
31778480
JS
5355 wxArrayInt childExtents;
5356 wxArrayInt* p;
5357 if (partialExtents)
5358 p = & childExtents;
5359 else
5360 p = NULL;
5361
a70eb13e
JS
5362 int maxDescent = 0;
5363 int maxAscent = 0;
5364 int maxLineHeight = 0;
5365
5d7836c4
JS
5366 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5367 while (node)
5368 {
5369 wxRichTextObject* child = node->GetData();
5370 if (!child->GetRange().IsOutside(range))
5371 {
cdaed652 5372 // Floating objects have a zero size within the paragraph.
e12b91a3 5373 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
cdaed652
VZ
5374 {
5375 if (partialExtents)
5376 {
5377 int lastSize;
5378 if (partialExtents->GetCount() > 0)
5379 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5380 else
5381 lastSize = 0;
5382
5383 partialExtents->Add(0 /* zero size */ + lastSize);
5384 }
5385 }
5386 else
5387 {
603f702b 5388 wxSize childSize;
4f3d5bc0 5389
603f702b
JS
5390 wxRichTextRange rangeToUse = range;
5391 rangeToUse.LimitTo(child->GetRange());
603f702b 5392 int childDescent = 0;
31778480 5393
7c9fdebe 5394 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
603f702b
JS
5395 // but it's only going to be used after caching has taken place.
5396 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5397 {
603f702b
JS
5398 childDescent = child->GetDescent();
5399 childSize = child->GetCachedSize();
2f45f554 5400
a70eb13e
JS
5401 if (childDescent == 0)
5402 {
5403 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5404 }
5405 else
5406 {
5407 maxDescent = wxMax(maxDescent, childDescent);
5408 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5409 }
5410
5411 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5412
5413 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5414 sz.x += childSize.x;
a70eb13e 5415 descent = maxDescent;
603f702b
JS
5416 }
5417 else if (child->IsTopLevel())
31778480 5418 {
603f702b
JS
5419 childDescent = child->GetDescent();
5420 childSize = child->GetCachedSize();
31778480 5421
a70eb13e
JS
5422 if (childDescent == 0)
5423 {
5424 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5425 }
5426 else
5427 {
5428 maxDescent = wxMax(maxDescent, childDescent);
5429 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5430 }
5431
5432 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5433
5434 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5435 sz.x += childSize.x;
a70eb13e
JS
5436 descent = maxDescent;
5437
5438 // FIXME: this won't change the original values.
5439 // Should we be calling GetRangeSize above instead of using cached values?
5440#if 0
603f702b 5441 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5442 {
603f702b
JS
5443 child->SetCachedSize(childSize);
5444 child->SetDescent(childDescent);
31778480 5445 }
a70eb13e 5446#endif
31778480 5447
603f702b
JS
5448 if (partialExtents)
5449 {
5450 int lastSize;
5451 if (partialExtents->GetCount() > 0)
5452 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5453 else
5454 lastSize = 0;
5455
5456 partialExtents->Add(childSize.x + lastSize);
5457 }
5458 }
914a4e23 5459 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
603f702b 5460 {
a70eb13e
JS
5461 if (childDescent == 0)
5462 {
5463 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5464 }
5465 else
5466 {
5467 maxDescent = wxMax(maxDescent, childDescent);
5468 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5469 }
5470
5471 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5472
5473 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5474 sz.x += childSize.x;
a70eb13e 5475 descent = maxDescent;
603f702b
JS
5476
5477 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5478 {
5479 child->SetCachedSize(childSize);
5480 child->SetDescent(childDescent);
5481 }
5482
5483 if (partialExtents)
5484 {
5485 int lastSize;
5486 if (partialExtents->GetCount() > 0)
5487 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5488 else
5489 lastSize = 0;
5490
5491 size_t i;
5492 for (i = 0; i < childExtents.GetCount(); i++)
5493 {
5494 partialExtents->Add(childExtents[i] + lastSize);
5495 }
5496 }
5497 }
5498 }
5499
5500 if (p)
5501 p->Clear();
5d7836c4
JS
5502 }
5503
5504 node = node->GetNext();
5505 }
5506 size = sz;
5507 }
5508 else
5509 {
5510 // Use formatted data, with line breaks
5511 wxSize sz;
5512
5513 // We're going to loop through each line, and then for each line,
5514 // call GetRangeSize for the fragment that comprises that line.
5515 // Only we have to do that multiple times within the line, because
5516 // the line may be broken into pieces. For now ignore line break commands
5517 // (so we can assume that getting the unformatted size for a fragment
5518 // within a line is the actual size)
5519
5520 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5521 while (node)
5522 {
5523 wxRichTextLine* line = node->GetData();
1e967276
JS
5524 wxRichTextRange lineRange = line->GetAbsoluteRange();
5525 if (!lineRange.IsOutside(range))
5d7836c4 5526 {
a70eb13e
JS
5527 int maxDescent = 0;
5528 int maxAscent = 0;
5529 int maxLineHeight = 0;
5530 int maxLineWidth = 0;
7fe8059f 5531
5d7836c4
JS
5532 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5533 while (node2)
5534 {
5535 wxRichTextObject* child = node2->GetData();
7fe8059f 5536
e12b91a3 5537 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5d7836c4 5538 {
1e967276 5539 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5540 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5541 if (child->IsTopLevel())
5542 rangeToUse = child->GetOwnRange();
7fe8059f 5543
5d7836c4
JS
5544 wxSize childSize;
5545 int childDescent = 0;
914a4e23 5546 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
5d7836c4 5547 {
a70eb13e
JS
5548 if (childDescent == 0)
5549 {
5550 // Assume that if descent is zero, this child can occupy the full line height
5551 // and does not need space for the line's maximum descent. So we influence
5552 // the overall max line height only.
5553 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5554 }
5555 else
5556 {
5557 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5558 maxDescent = wxMax(maxAscent, childDescent);
5559 }
5560 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5561 maxLineWidth += childSize.x;
5d7836c4 5562 }
5d7836c4 5563 }
7fe8059f 5564
5d7836c4
JS
5565 node2 = node2->GetNext();
5566 }
5567
a70eb13e
JS
5568 descent = wxMax(descent, maxDescent);
5569
5d7836c4 5570 // Increase size by a line (TODO: paragraph spacing)
a70eb13e
JS
5571 sz.y += maxLineHeight;
5572 sz.x = wxMax(sz.x, maxLineWidth);
5d7836c4
JS
5573 }
5574 node = node->GetNext();
5575 }
5576 size = sz;
5577 }
5578 return true;
5579}
5580
5581/// Finds the absolute position and row height for the given character position
8db2e3ef 5582bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5583{
5584 if (index == -1)
5585 {
5586 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5587 if (line)
5588 *height = line->GetSize().y;
5589 else
5590 *height = dc.GetCharHeight();
5591
5592 // -1 means 'the start of the buffer'.
5593 pt = GetPosition();
5594 if (line)
5595 pt = pt + line->GetPosition();
5596
5d7836c4
JS
5597 return true;
5598 }
5599
5600 // The final position in a paragraph is taken to mean the position
5601 // at the start of the next paragraph.
5602 if (index == GetRange().GetEnd())
5603 {
5604 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5605 wxASSERT( parent != NULL );
5606
5607 // Find the height at the next paragraph, if any
5608 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5609 if (line)
5610 {
5611 *height = line->GetSize().y;
5612 pt = line->GetAbsolutePosition();
5613 }
5614 else
5615 {
5616 *height = dc.GetCharHeight();
5617 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5618 pt = wxPoint(indent, GetCachedSize().y);
5619 }
5620
5621 return true;
5622 }
5623
5624 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5625 return false;
5626
5627 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5628 while (node)
5629 {
5630 wxRichTextLine* line = node->GetData();
1e967276
JS
5631 wxRichTextRange lineRange = line->GetAbsoluteRange();
5632 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5633 {
5634 // If this is the last point in the line, and we're forcing the
5635 // returned value to be the start of the next line, do the required
5636 // thing.
1e967276 5637 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5638 {
5639 if (node->GetNext())
5640 {
5641 wxRichTextLine* nextLine = node->GetNext()->GetData();
5642 *height = nextLine->GetSize().y;
5643 pt = nextLine->GetAbsolutePosition();
5644 return true;
5645 }
5646 }
5647
5648 pt.y = line->GetPosition().y + GetPosition().y;
5649
1e967276 5650 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5651 wxSize rangeSize;
5652 int descent = 0;
5653
5654 // We find the size of the line up to this point,
5655 // then we can add this size to the line start position and
5656 // paragraph start position to find the actual position.
5657
8db2e3ef 5658 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5659 {
5660 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5661 *height = line->GetSize().y;
5662
5663 return true;
5664 }
5665
5666 }
5667
5668 node = node->GetNext();
5669 }
5670
5671 return false;
5672}
5673
5674/// Hit-testing: returns a flag indicating hit test details, plus
5675/// information about position
8db2e3ef 5676int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5677{
603f702b
JS
5678 if (!IsShown())
5679 return wxRICHTEXT_HITTEST_NONE;
5680
5681 // If we're in the top-level container, then we can return
5682 // a suitable hit test code even if the point is outside the container area,
5683 // so that we can position the caret sensibly even if we don't
5684 // click on valid content. If we're not at the top-level, and the point
5685 // is not within this paragraph object, then we don't want to stop more
5686 // precise hit-testing from working prematurely, so return immediately.
5687 // NEW STRATEGY: use the parent boundary to test whether we're in the
5688 // right region, not the paragraph, since the paragraph may be positioned
5689 // some way in from where the user clicks.
5690 {
5691 long tmpPos;
5692 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5693 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5694 return wxRICHTEXT_HITTEST_NONE;
5695 }
5696
5697 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5698 while (objNode)
5699 {
5700 wxRichTextObject* child = objNode->GetData();
7c9fdebe
JS
5701 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5702 // and also, if this seems composite but actually is marked as atomic,
5703 // don't recurse.
5704 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5705 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
603f702b
JS
5706 {
5707 {
8db2e3ef 5708 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5709 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5710 return hitTest;
5711 }
5712 }
5713
5714 objNode = objNode->GetNext();
5715 }
5716
5d7836c4
JS
5717 wxPoint paraPos = GetPosition();
5718
5719 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5720 while (node)
5721 {
5722 wxRichTextLine* line = node->GetData();
5723 wxPoint linePos = paraPos + line->GetPosition();
5724 wxSize lineSize = line->GetSize();
1e967276 5725 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5726
62381daa 5727 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5728 {
5729 if (pt.x < linePos.x)
5730 {
1e967276 5731 textPosition = lineRange.GetStart();
603f702b
JS
5732 *obj = FindObjectAtPosition(textPosition);
5733 *contextObj = GetContainer();
f262b25c 5734 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5735 }
5736 else if (pt.x >= (linePos.x + lineSize.x))
5737 {
1e967276 5738 textPosition = lineRange.GetEnd();
603f702b
JS
5739 *obj = FindObjectAtPosition(textPosition);
5740 *contextObj = GetContainer();
f262b25c 5741 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5742 }
5743 else
5744 {
2f45f554
JS
5745#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5746 wxArrayInt partialExtents;
5747
5748 wxSize paraSize;
5749 int paraDescent;
5750
5751 // This calculates the partial text extents
914a4e23 5752 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
2f45f554
JS
5753
5754 int lastX = linePos.x;
5755 size_t i;
5756 for (i = 0; i < partialExtents.GetCount(); i++)
5757 {
5758 int nextX = partialExtents[i] + linePos.x;
5759
5760 if (pt.x >= lastX && pt.x <= nextX)
5761 {
5762 textPosition = i + lineRange.GetStart(); // minus 1?
5763
603f702b
JS
5764 *obj = FindObjectAtPosition(textPosition);
5765 *contextObj = GetContainer();
5766
2f45f554
JS
5767 // So now we know it's between i-1 and i.
5768 // Let's see if we can be more precise about
5769 // which side of the position it's on.
5770
cdaed652 5771 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5772 if (pt.x >= midPoint)
5773 return wxRICHTEXT_HITTEST_AFTER;
5774 else
5775 return wxRICHTEXT_HITTEST_BEFORE;
5776 }
5777
5778 lastX = nextX;
5779 }
5780#else
5d7836c4
JS
5781 long i;
5782 int lastX = linePos.x;
1e967276 5783 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5784 {
5785 wxSize childSize;
5786 int descent = 0;
7fe8059f 5787
1e967276 5788 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5789
8db2e3ef 5790 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5791
5792 int nextX = childSize.x + linePos.x;
5793
5794 if (pt.x >= lastX && pt.x <= nextX)
5795 {
5796 textPosition = i;
5797
603f702b
JS
5798 *obj = FindObjectAtPosition(textPosition);
5799 *contextObj = GetContainer();
5800
5d7836c4
JS
5801 // So now we know it's between i-1 and i.
5802 // Let's see if we can be more precise about
5803 // which side of the position it's on.
5804
cdaed652 5805 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5806 if (pt.x >= midPoint)
5807 return wxRICHTEXT_HITTEST_AFTER;
5808 else
5809 return wxRICHTEXT_HITTEST_BEFORE;
5810 }
5811 else
5812 {
5813 lastX = nextX;
5814 }
5815 }
2f45f554 5816#endif
5d7836c4
JS
5817 }
5818 }
7fe8059f 5819
5d7836c4
JS
5820 node = node->GetNext();
5821 }
5822
5823 return wxRICHTEXT_HITTEST_NONE;
5824}
5825
5826/// Split an object at this position if necessary, and return
5827/// the previous object, or NULL if inserting at beginning.
5828wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5829{
5830 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5831 while (node)
5832 {
5833 wxRichTextObject* child = node->GetData();
5834
5835 if (pos == child->GetRange().GetStart())
5836 {
5837 if (previousObject)
4d551ad5
JS
5838 {
5839 if (node->GetPrevious())
5840 *previousObject = node->GetPrevious()->GetData();
5841 else
5842 *previousObject = NULL;
5843 }
5d7836c4
JS
5844
5845 return child;
5846 }
5847
5848 if (child->GetRange().Contains(pos))
5849 {
5850 // This should create a new object, transferring part of
5851 // the content to the old object and the rest to the new object.
5852 wxRichTextObject* newObject = child->DoSplit(pos);
5853
5854 // If we couldn't split this object, just insert in front of it.
5855 if (!newObject)
5856 {
5857 // Maybe this is an empty string, try the next one
5858 // return child;
5859 }
5860 else
5861 {
5862 // Insert the new object after 'child'
5863 if (node->GetNext())
5864 m_children.Insert(node->GetNext(), newObject);
5865 else
5866 m_children.Append(newObject);
5867 newObject->SetParent(this);
5868
5869 if (previousObject)
5870 *previousObject = child;
5871
5872 return newObject;
5873 }
5874 }
5875
5876 node = node->GetNext();
5877 }
5878 if (previousObject)
5879 *previousObject = NULL;
5880 return NULL;
5881}
5882
5883/// Move content to a list from obj on
5884void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5885{
5886 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5887 while (node)
5888 {
5889 wxRichTextObject* child = node->GetData();
5890 list.Append(child);
5891
5892 wxRichTextObjectList::compatibility_iterator oldNode = node;
5893
5894 node = node->GetNext();
5895
5896 m_children.DeleteNode(oldNode);
5897 }
5898}
5899
5900/// Add content back from list
5901void wxRichTextParagraph::MoveFromList(wxList& list)
5902{
09f14108 5903 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5904 {
5905 AppendChild((wxRichTextObject*) node->GetData());
5906 }
5907}
5908
5909/// Calculate range
5910void wxRichTextParagraph::CalculateRange(long start, long& end)
5911{
5912 wxRichTextCompositeObject::CalculateRange(start, end);
5913
5914 // Add one for end of paragraph
5915 end ++;
5916
5917 m_range.SetRange(start, end);
5918}
5919
5920/// Find the object at the given position
5921wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5922{
5923 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5924 while (node)
5925 {
5926 wxRichTextObject* obj = node->GetData();
603f702b
JS
5927 if (obj->GetRange().Contains(position) ||
5928 obj->GetRange().GetStart() == position ||
5929 obj->GetRange().GetEnd() == position)
5d7836c4 5930 return obj;
7fe8059f 5931
5d7836c4
JS
5932 node = node->GetNext();
5933 }
5934 return NULL;
5935}
5936
5937/// Get the plain text searching from the start or end of the range.
5938/// The resulting string may be shorter than the range given.
5939bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5940{
5941 text = wxEmptyString;
5942
5943 if (fromStart)
5944 {
5945 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5946 while (node)
5947 {
5948 wxRichTextObject* obj = node->GetData();
5949 if (!obj->GetRange().IsOutside(range))
5950 {
5951 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5952 if (textObj)
5953 {
5954 text += textObj->GetTextForRange(range);
5955 }
5956 else
043c0d58
JS
5957 {
5958 text += wxT(" ");
5959 }
5d7836c4
JS
5960 }
5961
5962 node = node->GetNext();
5963 }
5964 }
5965 else
5966 {
5967 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5968 while (node)
5969 {
5970 wxRichTextObject* obj = node->GetData();
5971 if (!obj->GetRange().IsOutside(range))
5972 {
5973 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5974 if (textObj)
5975 {
5976 text = textObj->GetTextForRange(range) + text;
5977 }
5978 else
043c0d58
JS
5979 {
5980 text = wxT(" ") + text;
5981 }
5d7836c4
JS
5982 }
5983
5984 node = node->GetPrevious();
5985 }
5986 }
5987
5988 return true;
5989}
5990
5991/// Find a suitable wrap position.
8db2e3ef 5992bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5993{
72945e24
JS
5994 if (range.GetLength() <= 0)
5995 return false;
5996
5d7836c4
JS
5997 // Find the first position where the line exceeds the available space.
5998 wxSize sz;
5d7836c4 5999 long breakPosition = range.GetEnd();
ecb5fbf1 6000
31778480
JS
6001#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
6002 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 6003 {
31778480 6004 int widthBefore;
5d7836c4 6005
31778480
JS
6006 if (range.GetStart() > GetRange().GetStart())
6007 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
6008 else
6009 widthBefore = 0;
6010
6011 size_t i;
43a0d1e1 6012 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 6013 {
31778480 6014 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 6015
72945e24 6016 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 6017 {
31778480
JS
6018 breakPosition = i-1;
6019 break;
ecb5fbf1 6020 }
5d7836c4 6021 }
31778480
JS
6022 }
6023 else
6024#endif
6025 {
6026 // Binary chop for speed
6027 long minPos = range.GetStart();
6028 long maxPos = range.GetEnd();
6029 while (true)
ecb5fbf1 6030 {
31778480
JS
6031 if (minPos == maxPos)
6032 {
6033 int descent = 0;
8db2e3ef 6034 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 6035
31778480
JS
6036 if (sz.x > availableSpace)
6037 breakPosition = minPos - 1;
6038 break;
6039 }
6040 else if ((maxPos - minPos) == 1)
ecb5fbf1 6041 {
31778480 6042 int descent = 0;
8db2e3ef 6043 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6044
6045 if (sz.x > availableSpace)
6046 breakPosition = minPos - 1;
6047 else
6048 {
8db2e3ef 6049 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6050 if (sz.x > availableSpace)
6051 breakPosition = maxPos-1;
6052 }
6053 break;
ecb5fbf1
JS
6054 }
6055 else
6056 {
31778480
JS
6057 long nextPos = minPos + ((maxPos - minPos) / 2);
6058
6059 int descent = 0;
8db2e3ef 6060 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6061
6062 if (sz.x > availableSpace)
6063 {
6064 maxPos = nextPos;
6065 }
6066 else
6067 {
6068 minPos = nextPos;
6069 }
ecb5fbf1
JS
6070 }
6071 }
5d7836c4
JS
6072 }
6073
6074 // Now we know the last position on the line.
6075 // Let's try to find a word break.
6076
6077 wxString plainText;
6078 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6079 {
ff76711f
JS
6080 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6081 if (newLinePos != wxNOT_FOUND)
5d7836c4 6082 {
ff76711f
JS
6083 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6084 }
6085 else
6086 {
6087 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
6088 int tabPos = plainText.Find(wxT('\t'), true);
6089 int pos = wxMax(spacePos, tabPos);
6090 if (pos != wxNOT_FOUND)
ff76711f 6091 {
31002e44 6092 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
6093 breakPosition = breakPosition - positionsFromEndOfString;
6094 }
5d7836c4
JS
6095 }
6096 }
6097
6098 wrapPosition = breakPosition;
6099
6100 return true;
6101}
6102
6103/// Get the bullet text for this paragraph.
6104wxString wxRichTextParagraph::GetBulletText()
6105{
6106 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6107 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6108 return wxEmptyString;
6109
6110 int number = GetAttributes().GetBulletNumber();
6111
6112 wxString text;
d2d0adc7 6113 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
6114 {
6115 text.Printf(wxT("%d"), number);
6116 }
6117 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6118 {
6119 // TODO: Unicode, and also check if number > 26
6120 text.Printf(wxT("%c"), (wxChar) (number+64));
6121 }
6122 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6123 {
6124 // TODO: Unicode, and also check if number > 26
6125 text.Printf(wxT("%c"), (wxChar) (number+96));
6126 }
6127 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6128 {
59509217 6129 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
6130 }
6131 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6132 {
59509217
JS
6133 text = wxRichTextDecimalToRoman(number);
6134 text.MakeLower();
5d7836c4
JS
6135 }
6136 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6137 {
d2d0adc7
JS
6138 text = GetAttributes().GetBulletText();
6139 }
3e541562 6140
d2d0adc7
JS
6141 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6142 {
6143 // The outline style relies on the text being computed statically,
6144 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6145 // should be stored in the attributes; if not, just use the number for this
6146 // level, as previously computed.
6147 if (!GetAttributes().GetBulletText().IsEmpty())
6148 text = GetAttributes().GetBulletText();
5d7836c4
JS
6149 }
6150
6151 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6152 {
6153 text = wxT("(") + text + wxT(")");
6154 }
d2d0adc7
JS
6155 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6156 {
6157 text = text + wxT(")");
6158 }
6159
5d7836c4
JS
6160 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6161 {
6162 text += wxT(".");
6163 }
6164
6165 return text;
6166}
6167
1e967276
JS
6168/// Allocate or reuse a line object
6169wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6170{
6171 if (pos < (int) m_cachedLines.GetCount())
6172 {
6173 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6174 line->Init(this);
6175 return line;
6176 }
6177 else
6178 {
6179 wxRichTextLine* line = new wxRichTextLine(this);
6180 m_cachedLines.Append(line);
6181 return line;
6182 }
6183}
6184
6185/// Clear remaining unused line objects, if any
6186bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6187{
6188 int cachedLineCount = m_cachedLines.GetCount();
6189 if ((int) cachedLineCount > lineCount)
6190 {
6191 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6192 {
6193 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6194 wxRichTextLine* line = node->GetData();
6195 m_cachedLines.Erase(node);
6196 delete line;
6197 }
6198 }
6199 return true;
6200}
6201
fe5aa22c
JS
6202/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6203/// retrieve the actual style.
603f702b 6204wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 6205{
24777478 6206 wxRichTextAttr attr;
603f702b 6207 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6208 if (buf)
6209 {
6210 attr = buf->GetBasicStyle();
603f702b
JS
6211 if (!includingBoxAttr)
6212 {
6213 attr.GetTextBoxAttr().Reset();
6214 // The background colour will be painted by the container, and we don't
6215 // want to unnecessarily overwrite the background when we're drawing text
6216 // because this may erase the guideline (which appears just under the text
6217 // if there's no padding).
6218 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6219 }
fe5aa22c
JS
6220 wxRichTextApplyStyle(attr, GetAttributes());
6221 }
6222 else
6223 attr = GetAttributes();
6224
6225 wxRichTextApplyStyle(attr, contentStyle);
6226 return attr;
6227}
6228
6229/// Get combined attributes of the base style and paragraph style.
603f702b 6230wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 6231{
24777478 6232 wxRichTextAttr attr;
603f702b 6233 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6234 if (buf)
6235 {
6236 attr = buf->GetBasicStyle();
603f702b
JS
6237 if (!includingBoxAttr)
6238 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
6239 wxRichTextApplyStyle(attr, GetAttributes());
6240 }
6241 else
6242 attr = GetAttributes();
6243
6244 return attr;
6245}
5d7836c4 6246
603f702b 6247// Create default tabstop array
cfa3b256
JS
6248void wxRichTextParagraph::InitDefaultTabs()
6249{
6250 // create a default tab list at 10 mm each.
6251 for (int i = 0; i < 20; ++i)
6252 {
6253 sm_defaultTabs.Add(i*100);
6254 }
6255}
6256
603f702b 6257// Clear default tabstop array
cfa3b256
JS
6258void wxRichTextParagraph::ClearDefaultTabs()
6259{
6260 sm_defaultTabs.Clear();
6261}
6262
c4168888 6263void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
6264{
6265 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6266 while (node)
6267 {
bec80f4f 6268 wxRichTextObject* anchored = node->GetData();
07d4142f 6269 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652 6270 {
c4168888
JS
6271 int x = 0;
6272 wxRichTextAttr parentAttr(GetAttributes());
6273 context.ApplyVirtualAttributes(parentAttr, this);
6274#if 1
6275 // 27-09-2012
6276 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6277
6278 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6279 parentAttr, anchored->GetAttributes(),
6280 parentRect, availableSpace,
6281 style);
6282 wxSize size = anchored->GetCachedSize();
6283#else
cdaed652 6284 wxSize size;
c4168888 6285 int descent = 0;
8db2e3ef 6286 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
c4168888 6287#endif
bec80f4f 6288
24777478 6289 int offsetY = 0;
603f702b 6290 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
6291 {
6292 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6293 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6294 {
6295 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6296 }
6297 }
bec80f4f 6298
24777478 6299 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6300
cdaed652 6301 /* Update the offset */
24777478
JS
6302 int newOffsetY = pos - rect.y;
6303 if (newOffsetY != offsetY)
6304 {
6305 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6306 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6307 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6308 }
cdaed652 6309
24777478 6310 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6311 x = rect.x;
24777478 6312 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6313 x = rect.x + rect.width - size.x;
24777478 6314
c4168888
JS
6315 //anchored->SetPosition(wxPoint(x, pos));
6316 anchored->Move(wxPoint(x, pos)); // should move children
cdaed652
VZ
6317 anchored->SetCachedSize(size);
6318 floatCollector->CollectFloat(this, anchored);
6319 }
6320
6321 node = node->GetNext();
6322 }
6323}
6324
603f702b 6325// Get the first position from pos that has a line break character.
ff76711f
JS
6326long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6327{
6328 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6329 while (node)
6330 {
6331 wxRichTextObject* obj = node->GetData();
6332 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6333 {
6334 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6335 if (textObj)
6336 {
6337 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6338 if (breakPos > -1)
6339 return breakPos;
6340 }
6341 }
6342 node = node->GetNext();
6343 }
6344 return -1;
6345}
cfa3b256 6346
5d7836c4
JS
6347/*!
6348 * wxRichTextLine
6349 * This object represents a line in a paragraph, and stores
6350 * offsets from the start of the paragraph representing the
6351 * start and end positions of the line.
6352 */
6353
6354wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6355{
1e967276 6356 Init(parent);
5d7836c4
JS
6357}
6358
6359/// Initialisation
1e967276 6360void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6361{
1e967276
JS
6362 m_parent = parent;
6363 m_range.SetRange(-1, -1);
6364 m_pos = wxPoint(0, 0);
6365 m_size = wxSize(0, 0);
5d7836c4 6366 m_descent = 0;
2f45f554
JS
6367#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6368 m_objectSizes.Clear();
6369#endif
5d7836c4
JS
6370}
6371
6372/// Copy
6373void wxRichTextLine::Copy(const wxRichTextLine& obj)
6374{
6375 m_range = obj.m_range;
2f45f554
JS
6376#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6377 m_objectSizes = obj.m_objectSizes;
6378#endif
5d7836c4
JS
6379}
6380
6381/// Get the absolute object position
6382wxPoint wxRichTextLine::GetAbsolutePosition() const
6383{
6384 return m_parent->GetPosition() + m_pos;
6385}
6386
1e967276
JS
6387/// Get the absolute range
6388wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6389{
6390 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6391 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6392 return range;
6393}
6394
5d7836c4
JS
6395/*!
6396 * wxRichTextPlainText
6397 * This object represents a single piece of text.
6398 */
6399
6400IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6401
24777478 6402wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6403 wxRichTextObject(parent)
6404{
5d7836c4
JS
6405 if (style)
6406 SetAttributes(*style);
6407
6408 m_text = text;
6409}
6410
cfa3b256
JS
6411#define USE_KERNING_FIX 1
6412
4794d69c
JS
6413// If insufficient tabs are defined, this is the tab width used
6414#define WIDTH_FOR_DEFAULT_TABS 50
6415
5d7836c4 6416/// Draw the item
8db2e3ef 6417bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6418{
fe5aa22c
JS
6419 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6420 wxASSERT (para != NULL);
6421
603f702b 6422 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6423 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6424
6425 // Let's make the assumption for now that for content in a paragraph, including
6426 // text, we never have a discontinuous selection. So we only deal with a
6427 // single range.
6428 wxRichTextRange selectionRange;
6429 if (selection.IsValid())
6430 {
6431 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6432 if (selectionRanges.GetCount() > 0)
6433 selectionRange = selectionRanges[0];
6434 else
6435 selectionRange = wxRICHTEXT_NO_SELECTION;
6436 }
6437 else
6438 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6439
5d7836c4
JS
6440 int offset = GetRange().GetStart();
6441
ff76711f 6442 wxString str = m_text;
f7667b84
JS
6443 if (context.HasVirtualText(this))
6444 {
6445 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6446 str = m_text;
6447 }
6448
6449 // Replace line break characters with spaces
ff76711f
JS
6450 wxString toRemove = wxRichTextLineBreakChar;
6451 str.Replace(toRemove, wxT(" "));
d07f2e19 6452 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
c025e094 6453 str.MakeUpper();
3e541562 6454
5d7836c4 6455 long len = range.GetLength();
ff76711f 6456 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6457
5d7836c4
JS
6458 // Test for the optimized situations where all is selected, or none
6459 // is selected.
6460
30bf7630
JS
6461 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6462 wxCheckSetFont(dc, textFont);
6463 int charHeight = dc.GetCharHeight();
6464
6465 int x, y;
a1b806b9 6466 if ( textFont.IsOk() )
30bf7630 6467 {
d07f2e19
JS
6468 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6469 {
6470 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6471 wxCheckSetFont(dc, textFont);
6472 charHeight = dc.GetCharHeight();
6473 }
6474
30bf7630
JS
6475 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6476 {
32423dd8
JS
6477 if (textFont.IsUsingSizeInPixels())
6478 {
6479 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
4ba36292 6480 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
32423dd8
JS
6481 x = rect.x;
6482 y = rect.y;
6483 }
6484 else
6485 {
6486 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6487 textFont.SetPointSize(static_cast<int>(size));
32423dd8
JS
6488 x = rect.x;
6489 y = rect.y;
6490 }
30bf7630
JS
6491 wxCheckSetFont(dc, textFont);
6492 }
6493 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6494 {
32423dd8
JS
6495 if (textFont.IsUsingSizeInPixels())
6496 {
6497 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6498 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6499 x = rect.x;
4ba36292 6500 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6501 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6502 }
6503 else
6504 {
6505 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6506 textFont.SetPointSize(static_cast<int>(size));
32423dd8 6507 x = rect.x;
4ba36292 6508 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6509 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6510 }
30bf7630
JS
6511 wxCheckSetFont(dc, textFont);
6512 }
6513 else
6514 {
6515 x = rect.x;
6516 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6517 }
6518 }
6519 else
6520 {
6521 x = rect.x;
6522 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6523 }
5d7836c4 6524
603f702b
JS
6525 // TODO: new selection code
6526
5d7836c4
JS
6527 // (a) All selected.
6528 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6529 {
fe5aa22c 6530 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6531 }
6532 // (b) None selected.
6533 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6534 {
6535 // Draw all unselected
fe5aa22c 6536 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6537 }
6538 else
6539 {
6540 // (c) Part selected, part not
6541 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6542
04ee05f9 6543 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6544
5d7836c4
JS
6545 // 1. Initial unselected chunk, if any, up until start of selection.
6546 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6547 {
6548 int r1 = range.GetStart();
6549 int s1 = selectionRange.GetStart()-1;
6550 int fragmentLen = s1 - r1 + 1;
6551 if (fragmentLen < 0)
af588446 6552 {
5d7836c4 6553 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6554 }
ff76711f 6555 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6556
fe5aa22c 6557 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6558
6559#if USE_KERNING_FIX
6560 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6561 {
6562 // Compensate for kerning difference
ff76711f
JS
6563 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6564 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6565
cfa3b256
JS
6566 wxCoord w1, h1, w2, h2, w3, h3;
6567 dc.GetTextExtent(stringFragment, & w1, & h1);
6568 dc.GetTextExtent(stringFragment2, & w2, & h2);
6569 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6570
cfa3b256
JS
6571 int kerningDiff = (w1 + w3) - w2;
6572 x = x - kerningDiff;
6573 }
6574#endif
5d7836c4
JS
6575 }
6576
6577 // 2. Selected chunk, if any.
6578 if (selectionRange.GetEnd() >= range.GetStart())
6579 {
6580 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6581 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6582
6583 int fragmentLen = s2 - s1 + 1;
6584 if (fragmentLen < 0)
af588446 6585 {
5d7836c4 6586 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6587 }
ff76711f 6588 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6589
fe5aa22c 6590 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6591
6592#if USE_KERNING_FIX
6593 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6594 {
6595 // Compensate for kerning difference
ff76711f
JS
6596 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6597 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6598
cfa3b256
JS
6599 wxCoord w1, h1, w2, h2, w3, h3;
6600 dc.GetTextExtent(stringFragment, & w1, & h1);
6601 dc.GetTextExtent(stringFragment2, & w2, & h2);
6602 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6603
cfa3b256
JS
6604 int kerningDiff = (w1 + w3) - w2;
6605 x = x - kerningDiff;
6606 }
6607#endif
5d7836c4
JS
6608 }
6609
6610 // 3. Remaining unselected chunk, if any
6611 if (selectionRange.GetEnd() < range.GetEnd())
6612 {
6613 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6614 int r2 = range.GetEnd();
6615
6616 int fragmentLen = r2 - s2 + 1;
6617 if (fragmentLen < 0)
af588446 6618 {
5d7836c4 6619 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6620 }
ff76711f 6621 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6622
fe5aa22c 6623 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6624 }
5d7836c4
JS
6625 }
6626
6627 return true;
6628}
61399247 6629
24777478 6630bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6631{
cfa3b256
JS
6632 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6633
6634 wxArrayInt tabArray;
6635 int tabCount;
6636 if (hasTabs)
ab14c7aa 6637 {
cfa3b256
JS
6638 if (attr.GetTabs().IsEmpty())
6639 tabArray = wxRichTextParagraph::GetDefaultTabs();
6640 else
6641 tabArray = attr.GetTabs();
6642 tabCount = tabArray.GetCount();
6643
6644 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6645 {
cfa3b256
JS
6646 int pos = tabArray[i];
6647 pos = ConvertTenthsMMToPixels(dc, pos);
6648 tabArray[i] = pos;
7f0d9d71
JS
6649 }
6650 }
cfa3b256
JS
6651 else
6652 tabCount = 0;
ab14c7aa 6653
cfa3b256
JS
6654 int nextTabPos = -1;
6655 int tabPos = -1;
7f0d9d71 6656 wxCoord w, h;
ab14c7aa 6657
cfa3b256 6658 if (selected)
ab14c7aa 6659 {
0ec6da02
JS
6660 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6661 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6662
ecb5fbf1
JS
6663 wxCheckSetBrush(dc, wxBrush(highlightColour));
6664 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6665 dc.SetTextForeground(highlightTextColour);
04ee05f9 6666 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6667 }
ab14c7aa
JS
6668 else
6669 {
fe5aa22c 6670 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6671
f0e9eda2
JS
6672 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6673 {
04ee05f9 6674 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6675 dc.SetTextBackground(attr.GetBackgroundColour());
6676 }
6677 else
04ee05f9 6678 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6679 }
3e541562 6680
925a662a 6681 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6682 while (hasTabs)
ab14c7aa
JS
6683 {
6684 // the string has a tab
7f0d9d71
JS
6685 // break up the string at the Tab
6686 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6687 str = str.AfterFirst(wxT('\t'));
6688 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6689 tabPos = x + w;
7f0d9d71 6690 bool not_found = true;
cfa3b256 6691 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6692 {
015d0446 6693 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6694
6695 // Find the next tab position.
6696 // Even if we're at the end of the tab array, we must still draw the chunk.
6697
6698 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6699 {
4794d69c
JS
6700 if (nextTabPos <= tabPos)
6701 {
6702 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6703 nextTabPos = tabPos + defaultTabWidth;
6704 }
6705
7f0d9d71 6706 not_found = false;
ab14c7aa
JS
6707 if (selected)
6708 {
cfa3b256 6709 w = nextTabPos - x;
7f0d9d71 6710 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6711 dc.DrawRectangle(selRect);
7f0d9d71
JS
6712 }
6713 dc.DrawText(stringChunk, x, y);
42688aea
JS
6714
6715 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6716 {
6717 wxPen oldPen = dc.GetPen();
ecb5fbf1 6718 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6719 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6720 wxCheckSetPen(dc, oldPen);
42688aea
JS
6721 }
6722
cfa3b256 6723 x = nextTabPos;
7f0d9d71
JS
6724 }
6725 }
cfa3b256 6726 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6727 }
61399247 6728
cfa3b256 6729 if (!str.IsEmpty())
ab14c7aa 6730 {
cfa3b256
JS
6731 dc.GetTextExtent(str, & w, & h);
6732 if (selected)
6733 {
6734 wxRect selRect(x, rect.y, w, rect.GetHeight());
6735 dc.DrawRectangle(selRect);
6736 }
6737 dc.DrawText(str, x, y);
42688aea
JS
6738
6739 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6740 {
6741 wxPen oldPen = dc.GetPen();
ecb5fbf1 6742 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6743 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6744 wxCheckSetPen(dc, oldPen);
42688aea
JS
6745 }
6746
cfa3b256 6747 x += w;
7f0d9d71 6748 }
5d7836c4 6749
7c9fdebe 6750 return true;
7f0d9d71 6751}
fe5aa22c 6752
5d7836c4 6753/// Lay the item out
8db2e3ef 6754bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6755{
ecb5fbf1
JS
6756 // Only lay out if we haven't already cached the size
6757 if (m_size.x == -1)
8db2e3ef 6758 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6759 m_maxSize = m_size;
6760 // Eventually we want to have a reasonable estimate of minimum size.
6761 m_minSize = wxSize(0, 0);
5d7836c4
JS
6762 return true;
6763}
6764
6765/// Copy
6766void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6767{
6768 wxRichTextObject::Copy(obj);
6769
6770 m_text = obj.m_text;
6771}
6772
6773/// Get/set the object size for the given range. Returns false if the range
6774/// is invalid for this object.
914a4e23 6775bool 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
6776{
6777 if (!range.IsWithin(GetRange()))
6778 return false;
6779
fe5aa22c
JS
6780 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6781 wxASSERT (para != NULL);
603f702b 6782
925a662a 6783 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6784
24777478 6785 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6786 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6787
5d7836c4
JS
6788 // Always assume unformatted text, since at this level we have no knowledge
6789 // of line breaks - and we don't need it, since we'll calculate size within
6790 // formatted text by doing it in chunks according to the line ranges
6791
30bf7630 6792 bool bScript(false);
44cc96a8 6793 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6794 if (font.IsOk())
30bf7630
JS
6795 {
6796 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6797 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6798 {
6799 wxFont textFont = font;
32423dd8
JS
6800 if (textFont.IsUsingSizeInPixels())
6801 {
6802 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6803 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6804 }
6805 else
6806 {
6807 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6808 textFont.SetPointSize(static_cast<int>(size));
6809 }
30bf7630
JS
6810 wxCheckSetFont(dc, textFont);
6811 bScript = true;
6812 }
d07f2e19
JS
6813 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6814 {
6815 wxFont textFont = font;
6816 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6817 wxCheckSetFont(dc, textFont);
6818 bScript = true;
6819 }
30bf7630
JS
6820 else
6821 {
6822 wxCheckSetFont(dc, font);
6823 }
6824 }
5d7836c4 6825
109bfc88 6826 bool haveDescent = false;
5d7836c4
JS
6827 int startPos = range.GetStart() - GetRange().GetStart();
6828 long len = range.GetLength();
3e541562 6829
ff76711f 6830 wxString str(m_text);
f7667b84
JS
6831 if (context.HasVirtualText(this))
6832 {
6833 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6834 str = m_text;
6835 }
6836
ff76711f
JS
6837 wxString toReplace = wxRichTextLineBreakChar;
6838 str.Replace(toReplace, wxT(" "));
6839
6840 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea 6841
d07f2e19 6842 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
42688aea
JS
6843 stringChunk.MakeUpper();
6844
5d7836c4 6845 wxCoord w, h;
7f0d9d71 6846 int width = 0;
cfa3b256 6847 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6848 {
6849 // the string has a tab
cfa3b256
JS
6850 wxArrayInt tabArray;
6851 if (textAttr.GetTabs().IsEmpty())
6852 tabArray = wxRichTextParagraph::GetDefaultTabs();
6853 else
6854 tabArray = textAttr.GetTabs();
ab14c7aa 6855
cfa3b256 6856 int tabCount = tabArray.GetCount();
41a85215 6857
cfa3b256 6858 for (int i = 0; i < tabCount; ++i)
61399247 6859 {
cfa3b256
JS
6860 int pos = tabArray[i];
6861 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6862 tabArray[i] = pos;
7f0d9d71 6863 }
41a85215 6864
cfa3b256 6865 int nextTabPos = -1;
61399247 6866
ab14c7aa
JS
6867 while (stringChunk.Find(wxT('\t')) >= 0)
6868 {
109bfc88
JS
6869 int absoluteWidth = 0;
6870
ab14c7aa 6871 // the string has a tab
7f0d9d71
JS
6872 // break up the string at the Tab
6873 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6874 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6875
31778480
JS
6876 if (partialExtents)
6877 {
109bfc88
JS
6878 int oldWidth;
6879 if (partialExtents->GetCount() > 0)
6880 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6881 else
6882 oldWidth = 0;
6883
31778480
JS
6884 // Add these partial extents
6885 wxArrayInt p;
6886 dc.GetPartialTextExtents(stringFragment, p);
6887 size_t j;
6888 for (j = 0; j < p.GetCount(); j++)
6889 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6890
6891 if (partialExtents->GetCount() > 0)
925a662a 6892 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6893 else
925a662a 6894 absoluteWidth = relativeX;
109bfc88
JS
6895 }
6896 else
6897 {
6898 dc.GetTextExtent(stringFragment, & w, & h);
6899 width += w;
603f702b 6900 absoluteWidth = width + relativeX;
109bfc88 6901 haveDescent = true;
31778480
JS
6902 }
6903
cfa3b256
JS
6904 bool notFound = true;
6905 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6906 {
cfa3b256 6907 nextTabPos = tabArray.Item(i);
4794d69c
JS
6908
6909 // Find the next tab position.
6910 // Even if we're at the end of the tab array, we must still process the chunk.
6911
6912 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6913 {
4794d69c
JS
6914 if (nextTabPos <= absoluteWidth)
6915 {
6916 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6917 nextTabPos = absoluteWidth + defaultTabWidth;
6918 }
6919
cfa3b256 6920 notFound = false;
925a662a 6921 width = nextTabPos - relativeX;
31778480
JS
6922
6923 if (partialExtents)
6924 partialExtents->Add(width);
7f0d9d71
JS
6925 }
6926 }
6927 }
6928 }
30bf7630 6929
31778480
JS
6930 if (!stringChunk.IsEmpty())
6931 {
31778480
JS
6932 if (partialExtents)
6933 {
109bfc88
JS
6934 int oldWidth;
6935 if (partialExtents->GetCount() > 0)
6936 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6937 else
6938 oldWidth = 0;
6939
31778480
JS
6940 // Add these partial extents
6941 wxArrayInt p;
6942 dc.GetPartialTextExtents(stringChunk, p);
6943 size_t j;
6944 for (j = 0; j < p.GetCount(); j++)
6945 partialExtents->Add(oldWidth + p[j]);
6946 }
109bfc88
JS
6947 else
6948 {
6949 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6950 width += w;
6951 haveDescent = true;
6952 }
6953 }
6954
6955 if (partialExtents)
6956 {
6957 int charHeight = dc.GetCharHeight();
6958 if ((*partialExtents).GetCount() > 0)
6959 w = (*partialExtents)[partialExtents->GetCount()-1];
6960 else
6961 w = 0;
6962 size = wxSize(w, charHeight);
6963 }
6964 else
6965 {
6966 size = wxSize(width, dc.GetCharHeight());
31778480 6967 }
30bf7630 6968
109bfc88
JS
6969 if (!haveDescent)
6970 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6971
30bf7630
JS
6972 if ( bScript )
6973 dc.SetFont(font);
6974
5d7836c4
JS
6975 return true;
6976}
6977
6978/// Do a split, returning an object containing the second part, and setting
6979/// the first part in 'this'.
6980wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6981{
ff76711f 6982 long index = pos - GetRange().GetStart();
3e541562 6983
28f92d74 6984 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6985 return NULL;
6986
6987 wxString firstPart = m_text.Mid(0, index);
6988 wxString secondPart = m_text.Mid(index);
6989
6990 m_text = firstPart;
6991
6992 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6993 newObject->SetAttributes(GetAttributes());
8db2e3ef 6994 newObject->SetProperties(GetProperties());
5d7836c4
JS
6995
6996 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6997 GetRange().SetEnd(pos-1);
3e541562 6998
5d7836c4
JS
6999 return newObject;
7000}
7001
7002/// Calculate range
7003void wxRichTextPlainText::CalculateRange(long start, long& end)
7004{
28f92d74 7005 end = start + m_text.length() - 1;
5d7836c4
JS
7006 m_range.SetRange(start, end);
7007}
7008
7009/// Delete range
7010bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
7011{
7012 wxRichTextRange r = range;
7013
7014 r.LimitTo(GetRange());
7015
7016 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
7017 {
7018 m_text.Empty();
7019 return true;
7020 }
7021
7022 long startIndex = r.GetStart() - GetRange().GetStart();
7023 long len = r.GetLength();
7024
7025 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
7026 return true;
7027}
7028
7029/// Get text for the given range.
7030wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
7031{
7032 wxRichTextRange r = range;
7033
7034 r.LimitTo(GetRange());
7035
7036 long startIndex = r.GetStart() - GetRange().GetStart();
7037 long len = r.GetLength();
7038
7039 return m_text.Mid(startIndex, len);
7040}
7041
7042/// Returns true if this object can merge itself with the given one.
f7667b84 7043bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
5d7836c4 7044{
f7667b84
JS
7045 // JACS 2013-01-27
7046 if (!context.GetVirtualAttributesEnabled())
7047 {
7048 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
7049 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
7050 }
7051 else
7052 {
7053 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7054 if (!otherObj || m_text.empty())
7055 return false;
7056
7057 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7058 return false;
7059
7060 // Check if differing virtual attributes makes it impossible to merge
7061 // these strings.
7062
7063 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7064 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7065 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7066 return true;
7067 else if (hasVirtualAttr1 != hasVirtualAttr2)
7068 return false;
7069 else
7070 {
7071 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7072 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7073 return virtualAttr1 == virtualAttr2;
7074 }
7075 }
5d7836c4
JS
7076}
7077
7078/// Returns true if this object merged itself with the given one.
7079/// The calling code will then delete the given object.
f7667b84 7080bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
5d7836c4
JS
7081{
7082 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7083 wxASSERT( textObject != NULL );
7084
7085 if (textObject)
7086 {
7087 m_text += textObject->GetText();
99404ab0 7088 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
7089 return true;
7090 }
7091 else
7092 return false;
7093}
7094
f7667b84
JS
7095bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7096{
7097 // If this object has any virtual attributes at all, whether for the whole object
7098 // or individual ones, we should try splitting it by calling Split.
7099 // Must be more than one character in order to be able to split.
7100 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7101}
7102
7103wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7104{
7105 int count = context.GetVirtualSubobjectAttributesCount(this);
7106 if (count > 0 && GetParent())
7107 {
7108 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7109 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7110 if (node)
7111 {
7112 const wxRichTextAttr emptyAttr;
7113 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7114
7115 wxArrayInt positions;
7116 wxRichTextAttrArray attributes;
7117 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7118 {
7119 wxASSERT(positions.GetCount() == attributes.GetCount());
7120
7121 // We will gather up runs of text with the same virtual attributes
7122
7123 int len = m_text.Length();
7124 int i = 0;
7125
7126 // runStart and runEnd represent the accumulated run with a consistent attribute
7127 // that hasn't yet been appended
7128 int runStart = -1;
7129 int runEnd = -1;
7130 wxRichTextAttr currentAttr;
7131 wxString text = m_text;
7132 wxRichTextPlainText* lastPlainText = this;
7133
7134 for (i = 0; i < (int) positions.GetCount(); i++)
7135 {
7136 int pos = positions[i];
7137 wxASSERT(pos >= 0 && pos < len);
7138 if (pos >= 0 && pos < len)
7139 {
7140 const wxRichTextAttr& attr = attributes[i];
7141
7142 if (pos == 0)
7143 {
7144 runStart = 0;
7145 currentAttr = attr;
7146 }
7147 // Check if there was a gap from the last known attribute and this.
7148 // In that case, we need to do something with the span of non-attributed text.
7149 else if ((pos-1) > runEnd)
7150 {
7151 if (runEnd == -1)
7152 {
7153 // We hadn't processed anything previously, so the previous run is from the text start
7154 // to just before this position. The current attribute remains empty.
7155 runStart = 0;
7156 runEnd = pos-1;
7157 }
7158 else
7159 {
7160 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7161 // then just extend the run.
7162 if (currentAttr.IsDefault())
7163 {
7164 runEnd = pos-1;
7165 }
7166 else
7167 {
7168 // We need to add an object, or reuse the existing one.
7169 if (runStart == 0)
7170 {
7171 lastPlainText = this;
7172 SetText(text.Mid(runStart, runEnd - runStart + 1));
7173 }
7174 else
7175 {
7176 wxRichTextPlainText* obj = new wxRichTextPlainText;
7177 lastPlainText = obj;
7178 obj->SetAttributes(GetAttributes());
7179 obj->SetProperties(GetProperties());
7180 obj->SetParent(parent);
7181
7182 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7183 if (next)
7184 parent->GetChildren().Insert(next, obj);
7185 else
7186 parent->GetChildren().Append(obj);
7187 }
7188
7189 runStart = runEnd+1;
7190 runEnd = pos-1;
7191
7192 currentAttr = emptyAttr;
7193 }
7194 }
7195 }
7196
7197 wxASSERT(runEnd == pos-1);
7198
7199 // Now we only have to deal with the previous run
7200 if (currentAttr == attr)
7201 {
7202 // If we still have the same attributes, then we
7203 // simply increase the run size.
7204 runEnd = pos;
7205 }
7206 else
7207 {
7208 if (runEnd >= 0)
7209 {
7210 // We need to add an object, or reuse the existing one.
7211 if (runStart == 0)
7212 {
7213 lastPlainText = this;
7214 SetText(text.Mid(runStart, runEnd - runStart + 1));
7215 }
7216 else
7217 {
7218 wxRichTextPlainText* obj = new wxRichTextPlainText;
7219 lastPlainText = obj;
7220 obj->SetAttributes(GetAttributes());
7221 obj->SetProperties(GetProperties());
7222 obj->SetParent(parent);
7223
7224 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7225 if (next)
7226 parent->GetChildren().Insert(next, obj);
7227 else
7228 parent->GetChildren().Append(obj);
7229 }
7230 }
7231
7232 runStart = pos;
7233 runEnd = pos;
7234
7235 currentAttr = attr;
7236 }
7237 }
7238 }
7239
7240 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7241 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7242 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7243 {
7244 // If the current attribute is empty, merge the run with the next fragment
7245 // which by definition (because it's not specified) has empty attributes.
7246 if (currentAttr.IsDefault())
7247 runEnd = (len-1);
7248
7249 if (runEnd < (len-1))
7250 {
7251 // We need to add an object, or reuse the existing one.
7252 if (runStart == 0)
7253 {
7254 lastPlainText = this;
7255 SetText(text.Mid(runStart, runEnd - runStart + 1));
7256 }
7257 else
7258 {
7259 wxRichTextPlainText* obj = new wxRichTextPlainText;
7260 lastPlainText = obj;
7261 obj->SetAttributes(GetAttributes());
7262 obj->SetProperties(GetProperties());
7263 obj->SetParent(parent);
7264
7265 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7266 if (next)
7267 parent->GetChildren().Insert(next, obj);
7268 else
7269 parent->GetChildren().Append(obj);
7270 }
7271
7272 runStart = runEnd+1;
7273 runEnd = (len-1);
7274 }
7275
7276 // Now the last, non-attributed fragment at the end, if any
7277 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7278 {
7279 wxASSERT(runStart != 0);
7280
7281 wxRichTextPlainText* obj = new wxRichTextPlainText;
7282 obj->SetAttributes(GetAttributes());
7283 obj->SetProperties(GetProperties());
7284 obj->SetParent(parent);
7285
7286 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7287 if (next)
7288 parent->GetChildren().Insert(next, obj);
7289 else
7290 parent->GetChildren().Append(obj);
7291
7292 lastPlainText = obj;
7293 }
7294 }
7295
7296 return lastPlainText;
7297 }
7298 }
7299 }
7300 return this;
7301}
7302
5d7836c4
JS
7303/// Dump to output stream for debugging
7304void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7305{
7306 wxRichTextObject::Dump(stream);
7307 stream << m_text << wxT("\n");
7308}
7309
ff76711f
JS
7310/// Get the first position from pos that has a line break character.
7311long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7312{
7313 int i;
7314 int len = m_text.length();
7315 int startPos = pos - m_range.GetStart();
7316 for (i = startPos; i < len; i++)
7317 {
7318 wxChar ch = m_text[i];
7319 if (ch == wxRichTextLineBreakChar)
7320 {
7321 return i + m_range.GetStart();
7322 }
7323 }
7324 return -1;
7325}
7326
5d7836c4
JS
7327/*!
7328 * wxRichTextBuffer
7329 * This is a kind of box, used to represent the whole buffer
7330 */
7331
7332IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7333
7c9fdebe
JS
7334wxList wxRichTextBuffer::sm_handlers;
7335wxList wxRichTextBuffer::sm_drawingHandlers;
7336wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7337wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7338int wxRichTextBuffer::sm_bulletRightMargin = 20;
7339float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
e12b91a3 7340bool wxRichTextBuffer::sm_floatingLayoutMode = true;
5d7836c4
JS
7341
7342/// Initialisation
7343void wxRichTextBuffer::Init()
7344{
7345 m_commandProcessor = new wxCommandProcessor;
7346 m_styleSheet = NULL;
7347 m_modified = false;
7348 m_batchedCommandDepth = 0;
7349 m_batchedCommand = NULL;
7350 m_suppressUndo = 0;
d2d0adc7 7351 m_handlerFlags = 0;
44219ff0 7352 m_scale = 1.0;
32423dd8
JS
7353 m_dimensionScale = 1.0;
7354 m_fontScale = 1.0;
f819ed5d 7355 SetMargins(4);
5d7836c4
JS
7356}
7357
7358/// Initialisation
7359wxRichTextBuffer::~wxRichTextBuffer()
7360{
7361 delete m_commandProcessor;
7362 delete m_batchedCommand;
7363
7364 ClearStyleStack();
d2d0adc7 7365 ClearEventHandlers();
5d7836c4
JS
7366}
7367
85d8909b 7368void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 7369{
85d8909b 7370 Reset();
3e541562 7371
5d7836c4 7372 GetCommandProcessor()->ClearCommands();
5d7836c4 7373
5d7836c4 7374 Modify(false);
1e967276 7375 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
7376}
7377
0ca07313
JS
7378void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7379{
7380 wxRichTextParagraphLayoutBox::Copy(obj);
7381
7382 m_styleSheet = obj.m_styleSheet;
7383 m_modified = obj.m_modified;
bec80f4f
JS
7384 m_batchedCommandDepth = 0;
7385 if (m_batchedCommand)
7386 delete m_batchedCommand;
7387 m_batchedCommand = NULL;
0ca07313 7388 m_suppressUndo = obj.m_suppressUndo;
603f702b 7389 m_invalidRange = obj.m_invalidRange;
32423dd8
JS
7390 m_dimensionScale = obj.m_dimensionScale;
7391 m_fontScale = obj.m_fontScale;
0ca07313
JS
7392}
7393
38f833b1
JS
7394/// Push style sheet to top of stack
7395bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7396{
7397 if (m_styleSheet)
7398 styleSheet->InsertSheet(m_styleSheet);
7399
7400 SetStyleSheet(styleSheet);
41a85215 7401
38f833b1
JS
7402 return true;
7403}
7404
7405/// Pop style sheet from top of stack
7406wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7407{
7408 if (m_styleSheet)
7409 {
7410 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7411 m_styleSheet = oldSheet->GetNextSheet();
7412 oldSheet->Unlink();
41a85215 7413
38f833b1
JS
7414 return oldSheet;
7415 }
7416 else
7417 return NULL;
7418}
7419
0ca07313
JS
7420/// Submit command to insert paragraphs
7421bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7422{
4e63bfb9 7423 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
7424}
7425
7426/// Submit command to insert paragraphs
4e63bfb9 7427bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
7428{
7429 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 7430
0ca07313 7431 action->GetNewParagraphs() = paragraphs;
59509217 7432
0ca07313
JS
7433 action->SetPosition(pos);
7434
603f702b 7435 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
7436 if (!paragraphs.GetPartialParagraph())
7437 range.SetEnd(range.GetEnd()+1);
7438
0ca07313 7439 // Set the range we'll need to delete in Undo
99404ab0 7440 action->SetRange(range);
0ca07313 7441
603f702b 7442 buffer->SubmitAction(action);
0ca07313
JS
7443
7444 return true;
7445}
7446
5d7836c4 7447/// Submit command to insert the given text
fe5aa22c 7448bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7449{
4e63bfb9 7450 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
7451}
7452
7453/// Submit command to insert the given text
4e63bfb9 7454bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7455{
7456 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7457
24777478
JS
7458 wxRichTextAttr* p = NULL;
7459 wxRichTextAttr paraAttr;
fe5aa22c
JS
7460 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7461 {
7c081bd2 7462 // Get appropriate paragraph style
603f702b 7463 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
7464 if (!paraAttr.IsDefault())
7465 p = & paraAttr;
7466 }
7467
fe5aa22c 7468 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 7469
603f702b 7470 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 7471
6636ef8d 7472 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
7473 {
7474 // Don't count the newline when undoing
7475 length --;
5d7836c4 7476 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 7477 }
6636ef8d 7478 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 7479 length --;
5d7836c4
JS
7480
7481 action->SetPosition(pos);
7482
7483 // Set the range we'll need to delete in Undo
0ca07313 7484 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 7485
603f702b 7486 buffer->SubmitAction(action);
7fe8059f 7487
5d7836c4
JS
7488 return true;
7489}
7490
7491/// Submit command to insert the given text
fe5aa22c 7492bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7493{
4e63bfb9 7494 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
7495}
7496
7497/// Submit command to insert the given text
4e63bfb9 7498bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7499{
7500 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7501
24777478
JS
7502 wxRichTextAttr* p = NULL;
7503 wxRichTextAttr paraAttr;
fe5aa22c
JS
7504 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7505 {
603f702b 7506 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
7507 if (!paraAttr.IsDefault())
7508 p = & paraAttr;
7509 }
7510
603f702b 7511 wxRichTextAttr attr(buffer->GetDefaultStyle());
32423dd8
JS
7512 // Don't include box attributes such as margins
7513 attr.GetTextBoxAttr().Reset();
7fe8059f
WS
7514
7515 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
7516 action->GetNewParagraphs().AppendChild(newPara);
7517 action->GetNewParagraphs().UpdateRanges();
7518 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
7519 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7520 long pos1 = pos;
7521
6c0ea513
JS
7522 if (p)
7523 newPara->SetAttributes(*p);
7524
c025e094
JS
7525 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7526 {
7527 if (para && para->GetRange().GetEnd() == pos)
7528 pos1 ++;
e2d0875a
JS
7529
7530 // Now see if we need to number the paragraph.
6c0ea513 7531 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
7532 {
7533 wxRichTextAttr numberingAttr;
7534 if (FindNextParagraphNumber(para, numberingAttr))
7535 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7536 }
c025e094
JS
7537 }
7538
5d7836c4
JS
7539 action->SetPosition(pos);
7540
99404ab0 7541 // Use the default character style
603f702b 7542 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
7543 {
7544 // Check whether the default style merely reflects the paragraph/basic style,
7545 // in which case don't apply it.
603f702b 7546 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
32423dd8 7547 defaultStyle.GetTextBoxAttr().Reset();
24777478 7548 wxRichTextAttr toApply;
c025e094
JS
7549 if (para)
7550 {
603f702b 7551 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 7552 wxRichTextAttr newAttr;
c025e094
JS
7553 // This filters out attributes that are accounted for by the current
7554 // paragraph/basic style
7555 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7556 }
7557 else
7558 toApply = defaultStyle;
7559
7560 if (!toApply.IsDefault())
7561 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7562 }
99404ab0 7563
5d7836c4 7564 // Set the range we'll need to delete in Undo
c025e094 7565 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 7566
603f702b 7567 buffer->SubmitAction(action);
7fe8059f 7568
5d7836c4
JS
7569 return true;
7570}
7571
7572/// Submit command to insert the given image
24777478
JS
7573bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7574 const wxRichTextAttr& textAttr)
5d7836c4 7575{
4e63bfb9 7576 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
7577}
7578
7579/// Submit command to insert the given image
4e63bfb9
JS
7580bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7581 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
7582 const wxRichTextAttr& textAttr)
7583{
7584 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7585
24777478
JS
7586 wxRichTextAttr* p = NULL;
7587 wxRichTextAttr paraAttr;
fe5aa22c
JS
7588 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7589 {
603f702b 7590 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
7591 if (!paraAttr.IsDefault())
7592 p = & paraAttr;
7593 }
7594
603f702b 7595 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 7596
32423dd8
JS
7597 // Don't include box attributes such as margins
7598 attr.GetTextBoxAttr().Reset();
7599
5d7836c4 7600 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
7601 if (p)
7602 newPara->SetAttributes(*p);
7603
5d7836c4
JS
7604 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7605 newPara->AppendChild(imageObject);
24777478 7606 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7607 action->GetNewParagraphs().AppendChild(newPara);
7608 action->GetNewParagraphs().UpdateRanges();
7609
7610 action->GetNewParagraphs().SetPartialParagraph(true);
7611
7612 action->SetPosition(pos);
7613
7614 // Set the range we'll need to delete in Undo
7615 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7616
603f702b 7617 buffer->SubmitAction(action);
7fe8059f 7618
5d7836c4
JS
7619 return true;
7620}
7621
cdaed652 7622// Insert an object with no change of it
603f702b
JS
7623wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7624{
4e63bfb9 7625 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7626}
7627
7628// Insert an object with no change of it
4e63bfb9 7629wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7630{
603f702b 7631 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7632
24777478
JS
7633 wxRichTextAttr* p = NULL;
7634 wxRichTextAttr paraAttr;
cdaed652
VZ
7635 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7636 {
603f702b 7637 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7638 if (!paraAttr.IsDefault())
7639 p = & paraAttr;
7640 }
7641
603f702b 7642 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652 7643
32423dd8
JS
7644 // Don't include box attributes such as margins
7645 attr.GetTextBoxAttr().Reset();
7646
cdaed652
VZ
7647 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7648 if (p)
7649 newPara->SetAttributes(*p);
7650
7651 newPara->AppendChild(object);
7652 action->GetNewParagraphs().AppendChild(newPara);
7653 action->GetNewParagraphs().UpdateRanges();
7654
7655 action->GetNewParagraphs().SetPartialParagraph(true);
7656
7657 action->SetPosition(pos);
7658
7659 // Set the range we'll need to delete in Undo
7660 action->SetRange(wxRichTextRange(pos, pos));
7661
603f702b 7662 buffer->SubmitAction(action);
cdaed652 7663
603f702b
JS
7664 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7665 return obj;
cdaed652 7666}
603f702b 7667
7c9fdebe
JS
7668wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7669 const wxRichTextProperties& properties,
7670 wxRichTextCtrl* ctrl, int flags,
7671 const wxRichTextAttr& textAttr)
7672{
7673 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7674
7675 wxRichTextAttr* p = NULL;
7676 wxRichTextAttr paraAttr;
7677 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7678 {
7679 paraAttr = GetStyleForNewParagraph(buffer, pos);
7680 if (!paraAttr.IsDefault())
7681 p = & paraAttr;
7682 }
7683
7684 wxRichTextAttr attr(buffer->GetDefaultStyle());
7685
32423dd8
JS
7686 // Don't include box attributes such as margins
7687 attr.GetTextBoxAttr().Reset();
7688
7c9fdebe
JS
7689 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7690 if (p)
7691 newPara->SetAttributes(*p);
7692
7693 wxRichTextField* fieldObject = new wxRichTextField();
7694 fieldObject->wxRichTextObject::SetProperties(properties);
7695 fieldObject->SetFieldType(fieldType);
7696 fieldObject->SetAttributes(textAttr);
7697 newPara->AppendChild(fieldObject);
7698 action->GetNewParagraphs().AppendChild(newPara);
7699 action->GetNewParagraphs().UpdateRanges();
7700 action->GetNewParagraphs().SetPartialParagraph(true);
7701 action->SetPosition(pos);
7702
7703 // Set the range we'll need to delete in Undo
7704 action->SetRange(wxRichTextRange(pos, pos));
7705
7706 buffer->SubmitAction(action);
7707
7708 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7709 return obj;
7710}
7711
fe5aa22c
JS
7712/// Get the style that is appropriate for a new paragraph at this position.
7713/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7714/// style.
603f702b 7715wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7716{
7717 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7718 if (para)
7719 {
24777478 7720 wxRichTextAttr attr;
d2d0adc7 7721 bool foundAttributes = false;
3e541562 7722
d2d0adc7 7723 // Look for a matching paragraph style
603f702b 7724 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7725 {
603f702b 7726 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7727 if (paraDef)
fe5aa22c 7728 {
caad0109
JS
7729 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7730 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7731 {
603f702b 7732 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7733 if (nextParaDef)
7734 {
7735 foundAttributes = true;
603f702b 7736 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7737 }
7738 }
3e541562 7739
d2d0adc7
JS
7740 // If we didn't find the 'next style', use this style instead.
7741 if (!foundAttributes)
7742 {
7743 foundAttributes = true;
603f702b 7744 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7745 }
fe5aa22c
JS
7746 }
7747 }
e2d0875a
JS
7748
7749 // Also apply list style if present
603f702b 7750 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7751 {
603f702b 7752 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7753 if (listDef)
7754 {
7755 int thisIndent = para->GetAttributes().GetLeftIndent();
7756 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7757
7758 // Apply the overall list style, and item style for this level
603f702b 7759 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7760 wxRichTextApplyStyle(attr, listStyle);
7761 attr.SetOutlineLevel(thisLevel);
7762 if (para->GetAttributes().HasBulletNumber())
7763 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7764 }
34b4899d 7765 }
e2d0875a 7766
d2d0adc7
JS
7767 if (!foundAttributes)
7768 {
7769 attr = para->GetAttributes();
7770 int flags = attr.GetFlags();
fe5aa22c 7771
d2d0adc7
JS
7772 // Eliminate character styles
7773 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7774 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7775 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7776 attr.SetFlags(flags);
7777 }
3e541562 7778
fe5aa22c
JS
7779 return attr;
7780 }
7781 else
24777478 7782 return wxRichTextAttr();
fe5aa22c
JS
7783}
7784
5d7836c4 7785/// Submit command to delete this range
12cc29c5 7786bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7787{
603f702b
JS
7788 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7789}
7790
7791/// Submit command to delete this range
7792bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7793{
7794 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7795
12cc29c5 7796 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7797
7798 // Set the range to delete
7799 action->SetRange(range);
7fe8059f 7800
5d7836c4
JS
7801 // Copy the fragment that we'll need to restore in Undo
7802 CopyFragment(range, action->GetOldParagraphs());
7803
6c0ea513
JS
7804 // See if we're deleting a paragraph marker, in which case we need to
7805 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7806 if (range.GetStart() == range.GetEnd())
5d7836c4 7807 {
6c0ea513
JS
7808 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7809 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7810 {
6c0ea513
JS
7811 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7812 if (nextPara && nextPara != para)
5d7836c4 7813 {
6c0ea513
JS
7814 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7815 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7816 }
7817 }
7818 }
7819
603f702b 7820 buffer->SubmitAction(action);
7fe8059f 7821
5d7836c4
JS
7822 return true;
7823}
7824
7825/// Collapse undo/redo commands
7826bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7827{
7828 if (m_batchedCommandDepth == 0)
7829 {
7830 wxASSERT(m_batchedCommand == NULL);
7831 if (m_batchedCommand)
7832 {
0745f364 7833 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7834 }
7835 m_batchedCommand = new wxRichTextCommand(cmdName);
7836 }
7837
7fe8059f 7838 m_batchedCommandDepth ++;
5d7836c4
JS
7839
7840 return true;
7841}
7842
7843/// Collapse undo/redo commands
7844bool wxRichTextBuffer::EndBatchUndo()
7845{
7846 m_batchedCommandDepth --;
7847
7848 wxASSERT(m_batchedCommandDepth >= 0);
7849 wxASSERT(m_batchedCommand != NULL);
7850
7851 if (m_batchedCommandDepth == 0)
7852 {
0745f364 7853 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7854 m_batchedCommand = NULL;
7855 }
7856
7857 return true;
7858}
7859
7860/// Submit immediately, or delay according to whether collapsing is on
7861bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7862{
cc2aecde
JS
7863 if (action && !action->GetNewParagraphs().IsEmpty())
7864 PrepareContent(action->GetNewParagraphs());
7865
5d7836c4 7866 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7867 {
7868 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7869 cmd->AddAction(action);
7870 cmd->Do();
7871 cmd->GetActions().Clear();
7872 delete cmd;
7873
5d7836c4 7874 m_batchedCommand->AddAction(action);
0745f364 7875 }
5d7836c4
JS
7876 else
7877 {
7878 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7879 cmd->AddAction(action);
7880
7881 // Only store it if we're not suppressing undo.
7882 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7883 }
7884
7885 return true;
7886}
7887
7888/// Begin suppressing undo/redo commands.
7889bool wxRichTextBuffer::BeginSuppressUndo()
7890{
7fe8059f 7891 m_suppressUndo ++;
5d7836c4
JS
7892
7893 return true;
7894}
7895
7896/// End suppressing undo/redo commands.
7897bool wxRichTextBuffer::EndSuppressUndo()
7898{
7fe8059f 7899 m_suppressUndo --;
5d7836c4
JS
7900
7901 return true;
7902}
7903
7904/// Begin using a style
24777478 7905bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7906{
24777478 7907 wxRichTextAttr newStyle(GetDefaultStyle());
32423dd8 7908 newStyle.GetTextBoxAttr().Reset();
5d7836c4
JS
7909
7910 // Save the old default style
32423dd8 7911 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
5d7836c4
JS
7912
7913 wxRichTextApplyStyle(newStyle, style);
7914 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7915
7916 SetDefaultStyle(newStyle);
7917
5d7836c4
JS
7918 return true;
7919}
7920
7921/// End the style
7922bool wxRichTextBuffer::EndStyle()
7923{
63886f6d 7924 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7925 {
7926 wxLogDebug(_("Too many EndStyle calls!"));
7927 return false;
7928 }
7929
09f14108 7930 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7931 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7932 m_attributeStack.Erase(node);
5d7836c4
JS
7933
7934 SetDefaultStyle(*attr);
7935
7936 delete attr;
7937 return true;
7938}
7939
7940/// End all styles
7941bool wxRichTextBuffer::EndAllStyles()
7942{
7943 while (m_attributeStack.GetCount() != 0)
7944 EndStyle();
7945 return true;
7946}
7947
7948/// Clear the style stack
7949void wxRichTextBuffer::ClearStyleStack()
7950{
09f14108 7951 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7952 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7953 m_attributeStack.Clear();
7954}
7955
7956/// Begin using bold
7957bool wxRichTextBuffer::BeginBold()
7958{
24777478 7959 wxRichTextAttr attr;
7d76fbd5 7960 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7961
5d7836c4
JS
7962 return BeginStyle(attr);
7963}
7964
7965/// Begin using italic
7966bool wxRichTextBuffer::BeginItalic()
7967{
24777478 7968 wxRichTextAttr attr;
7d76fbd5 7969 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7970
5d7836c4
JS
7971 return BeginStyle(attr);
7972}
7973
7974/// Begin using underline
7975bool wxRichTextBuffer::BeginUnderline()
7976{
24777478 7977 wxRichTextAttr attr;
44cc96a8 7978 attr.SetFontUnderlined(true);
7fe8059f 7979
5d7836c4
JS
7980 return BeginStyle(attr);
7981}
7982
7983/// Begin using point size
7984bool wxRichTextBuffer::BeginFontSize(int pointSize)
7985{
24777478 7986 wxRichTextAttr attr;
44cc96a8 7987 attr.SetFontSize(pointSize);
7fe8059f 7988
5d7836c4
JS
7989 return BeginStyle(attr);
7990}
7991
7992/// Begin using this font
7993bool wxRichTextBuffer::BeginFont(const wxFont& font)
7994{
24777478 7995 wxRichTextAttr attr;
5d7836c4 7996 attr.SetFont(font);
7fe8059f 7997
5d7836c4
JS
7998 return BeginStyle(attr);
7999}
8000
8001/// Begin using this colour
8002bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
8003{
24777478 8004 wxRichTextAttr attr;
5d7836c4
JS
8005 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
8006 attr.SetTextColour(colour);
7fe8059f 8007
5d7836c4
JS
8008 return BeginStyle(attr);
8009}
8010
8011/// Begin using alignment
8012bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
8013{
24777478 8014 wxRichTextAttr attr;
5d7836c4
JS
8015 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
8016 attr.SetAlignment(alignment);
7fe8059f 8017
5d7836c4
JS
8018 return BeginStyle(attr);
8019}
8020
8021/// Begin left indent
8022bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
8023{
24777478 8024 wxRichTextAttr attr;
5d7836c4
JS
8025 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
8026 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8027
5d7836c4
JS
8028 return BeginStyle(attr);
8029}
8030
8031/// Begin right indent
8032bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
8033{
24777478 8034 wxRichTextAttr attr;
5d7836c4
JS
8035 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
8036 attr.SetRightIndent(rightIndent);
7fe8059f 8037
5d7836c4
JS
8038 return BeginStyle(attr);
8039}
8040
8041/// Begin paragraph spacing
8042bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
8043{
8044 long flags = 0;
8045 if (before != 0)
8046 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
8047 if (after != 0)
8048 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
8049
24777478 8050 wxRichTextAttr attr;
5d7836c4
JS
8051 attr.SetFlags(flags);
8052 attr.SetParagraphSpacingBefore(before);
8053 attr.SetParagraphSpacingAfter(after);
7fe8059f 8054
5d7836c4
JS
8055 return BeginStyle(attr);
8056}
8057
8058/// Begin line spacing
8059bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8060{
24777478 8061 wxRichTextAttr attr;
5d7836c4
JS
8062 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8063 attr.SetLineSpacing(lineSpacing);
7fe8059f 8064
5d7836c4
JS
8065 return BeginStyle(attr);
8066}
8067
8068/// Begin numbered bullet
8069bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8070{
24777478 8071 wxRichTextAttr attr;
f089713f 8072 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8073 attr.SetBulletStyle(bulletStyle);
8074 attr.SetBulletNumber(bulletNumber);
8075 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8076
5d7836c4
JS
8077 return BeginStyle(attr);
8078}
8079
8080/// Begin symbol bullet
d2d0adc7 8081bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 8082{
24777478 8083 wxRichTextAttr attr;
f089713f 8084 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8085 attr.SetBulletStyle(bulletStyle);
8086 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 8087 attr.SetBulletText(symbol);
7fe8059f 8088
5d7836c4
JS
8089 return BeginStyle(attr);
8090}
8091
f089713f
JS
8092/// Begin standard bullet
8093bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8094{
24777478 8095 wxRichTextAttr attr;
f089713f
JS
8096 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8097 attr.SetBulletStyle(bulletStyle);
8098 attr.SetLeftIndent(leftIndent, leftSubIndent);
8099 attr.SetBulletName(bulletName);
8100
8101 return BeginStyle(attr);
8102}
8103
5d7836c4
JS
8104/// Begin named character style
8105bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8106{
8107 if (GetStyleSheet())
8108 {
8109 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8110 if (def)
8111 {
24777478 8112 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8113 return BeginStyle(attr);
8114 }
8115 }
8116 return false;
8117}
8118
8119/// Begin named paragraph style
8120bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8121{
8122 if (GetStyleSheet())
8123 {
8124 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8125 if (def)
8126 {
24777478 8127 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8128 return BeginStyle(attr);
8129 }
8130 }
8131 return false;
8132}
8133
f089713f
JS
8134/// Begin named list style
8135bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8136{
8137 if (GetStyleSheet())
8138 {
8139 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8140 if (def)
8141 {
24777478 8142 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
8143
8144 attr.SetBulletNumber(number);
8145
8146 return BeginStyle(attr);
8147 }
8148 }
8149 return false;
8150}
8151
d2d0adc7
JS
8152/// Begin URL
8153bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8154{
24777478 8155 wxRichTextAttr attr;
d2d0adc7
JS
8156
8157 if (!characterStyle.IsEmpty() && GetStyleSheet())
8158 {
8159 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8160 if (def)
8161 {
336d8ae9 8162 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
8163 }
8164 }
8165 attr.SetURL(url);
8166
8167 return BeginStyle(attr);
8168}
8169
5d7836c4
JS
8170/// Adds a handler to the end
8171void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8172{
8173 sm_handlers.Append(handler);
8174}
8175
8176/// Inserts a handler at the front
8177void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8178{
8179 sm_handlers.Insert( handler );
8180}
8181
8182/// Removes a handler
8183bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8184{
8185 wxRichTextFileHandler *handler = FindHandler(name);
8186 if (handler)
8187 {
8188 sm_handlers.DeleteObject(handler);
8189 delete handler;
8190 return true;
8191 }
8192 else
8193 return false;
8194}
8195
8196/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
8197wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8198 wxRichTextFileType imageType)
5d7836c4
JS
8199{
8200 if (imageType != wxRICHTEXT_TYPE_ANY)
8201 return FindHandler(imageType);
0ca07313 8202 else if (!filename.IsEmpty())
5d7836c4
JS
8203 {
8204 wxString path, file, ext;
a51e601e 8205 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
8206 return FindHandler(ext, imageType);
8207 }
0ca07313
JS
8208 else
8209 return NULL;
5d7836c4
JS
8210}
8211
8212
8213/// Finds a handler by name
8214wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8215{
8216 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8217 while (node)
8218 {
8219 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8220 if (handler->GetName().Lower() == name.Lower()) return handler;
8221
8222 node = node->GetNext();
8223 }
8224 return NULL;
8225}
8226
8227/// Finds a handler by extension and type
d75a69e8 8228wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
8229{
8230 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8231 while (node)
8232 {
8233 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8234 if ( handler->GetExtension().Lower() == extension.Lower() &&
8235 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8236 return handler;
8237 node = node->GetNext();
8238 }
8239 return 0;
8240}
8241
8242/// Finds a handler by type
d75a69e8 8243wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
8244{
8245 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8246 while (node)
8247 {
8248 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8249 if (handler->GetType() == type) return handler;
8250 node = node->GetNext();
8251 }
8252 return NULL;
8253}
8254
8255void wxRichTextBuffer::InitStandardHandlers()
8256{
8257 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8258 AddHandler(new wxRichTextPlainTextHandler);
8259}
8260
8261void wxRichTextBuffer::CleanUpHandlers()
8262{
8263 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8264 while (node)
8265 {
8266 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8267 wxList::compatibility_iterator next = node->GetNext();
8268 delete handler;
8269 node = next;
8270 }
8271
8272 sm_handlers.Clear();
8273}
8274
1e967276 8275wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 8276{
1e967276
JS
8277 if (types)
8278 types->Clear();
8279
5d7836c4
JS
8280 wxString wildcard;
8281
8282 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8283 int count = 0;
8284 while (node)
8285 {
8286 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 8287 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
8288 {
8289 if (combine)
8290 {
8291 if (count > 0)
8292 wildcard += wxT(";");
8293 wildcard += wxT("*.") + handler->GetExtension();
8294 }
8295 else
8296 {
8297 if (count > 0)
8298 wildcard += wxT("|");
8299 wildcard += handler->GetName();
8300 wildcard += wxT(" ");
8301 wildcard += _("files");
8302 wildcard += wxT(" (*.");
8303 wildcard += handler->GetExtension();
8304 wildcard += wxT(")|*.");
8305 wildcard += handler->GetExtension();
1e967276
JS
8306 if (types)
8307 types->Add(handler->GetType());
5d7836c4
JS
8308 }
8309 count ++;
8310 }
8311
8312 node = node->GetNext();
8313 }
8314
8315 if (combine)
8316 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8317 return wildcard;
8318}
8319
8320/// Load a file
d75a69e8 8321bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8322{
8323 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8324 if (handler)
1e967276 8325 {
24777478 8326 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8327 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8328 bool success = handler->LoadFile(this, filename);
8329 Invalidate(wxRICHTEXT_ALL);
8330 return success;
8331 }
5d7836c4
JS
8332 else
8333 return false;
8334}
8335
8336/// Save a file
d75a69e8 8337bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8338{
8339 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8340 if (handler)
d2d0adc7
JS
8341 {
8342 handler->SetFlags(GetHandlerFlags());
5d7836c4 8343 return handler->SaveFile(this, filename);
d2d0adc7 8344 }
5d7836c4
JS
8345 else
8346 return false;
8347}
8348
8349/// Load from a stream
d75a69e8 8350bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8351{
8352 wxRichTextFileHandler* handler = FindHandler(type);
8353 if (handler)
1e967276 8354 {
24777478 8355 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8356 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8357 bool success = handler->LoadFile(this, stream);
8358 Invalidate(wxRICHTEXT_ALL);
8359 return success;
8360 }
5d7836c4
JS
8361 else
8362 return false;
8363}
8364
8365/// Save to a stream
d75a69e8 8366bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8367{
8368 wxRichTextFileHandler* handler = FindHandler(type);
8369 if (handler)
d2d0adc7
JS
8370 {
8371 handler->SetFlags(GetHandlerFlags());
5d7836c4 8372 return handler->SaveFile(this, stream);
d2d0adc7 8373 }
5d7836c4
JS
8374 else
8375 return false;
8376}
8377
8378/// Copy the range to the clipboard
8379bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8380{
8381 bool success = false;
603f702b
JS
8382 wxRichTextParagraphLayoutBox* container = this;
8383 if (GetRichTextCtrl())
8384 container = GetRichTextCtrl()->GetFocusObject();
8385
11ef729d 8386#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 8387
d2142335 8388 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 8389 {
0ca07313
JS
8390 wxTheClipboard->Clear();
8391
8392 // Add composite object
8393
8394 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8395
8396 {
603f702b 8397 wxString text = container->GetTextForRange(range);
0ca07313
JS
8398
8399#ifdef __WXMSW__
8400 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8401#endif
8402
8403 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8404 }
8405
8406 // Add rich text buffer data object. This needs the XML handler to be present.
8407
8408 if (FindHandler(wxRICHTEXT_TYPE_XML))
8409 {
8410 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 8411 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
8412
8413 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8414 }
8415
8416 if (wxTheClipboard->SetData(compositeObject))
8417 success = true;
8418
5d7836c4
JS
8419 wxTheClipboard->Close();
8420 }
0ca07313 8421
39a1c2f2
WS
8422#else
8423 wxUnusedVar(range);
8424#endif
5d7836c4
JS
8425 return success;
8426}
8427
8428/// Paste the clipboard content to the buffer
8429bool wxRichTextBuffer::PasteFromClipboard(long position)
8430{
8431 bool success = false;
603f702b
JS
8432 wxRichTextParagraphLayoutBox* container = this;
8433 if (GetRichTextCtrl())
8434 container = GetRichTextCtrl()->GetFocusObject();
8435
11ef729d 8436#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
8437 if (CanPasteFromClipboard())
8438 {
8439 if (wxTheClipboard->Open())
8440 {
0ca07313
JS
8441 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8442 {
8443 wxRichTextBufferDataObject data;
8444 wxTheClipboard->GetData(data);
8445 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8446 if (richTextBuffer)
8447 {
4e63bfb9 8448 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 8449 if (GetRichTextCtrl())
603f702b 8450 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
8451 delete richTextBuffer;
8452 }
8453 }
f7d83f24 8454 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
8455 #if wxUSE_UNICODE
8456 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8457 #endif
f7d83f24 8458 )
5d7836c4
JS
8459 {
8460 wxTextDataObject data;
8461 wxTheClipboard->GetData(data);
8462 wxString text(data.GetText());
c21f3211
JS
8463#ifdef __WXMSW__
8464 wxString text2;
8465 text2.Alloc(text.Length()+1);
8466 size_t i;
8467 for (i = 0; i < text.Length(); i++)
8468 {
8469 wxChar ch = text[i];
8470 if (ch != wxT('\r'))
8471 text2 += ch;
8472 }
8473#else
8474 wxString text2 = text;
8475#endif
4e63bfb9 8476 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 8477
62381daa
JS
8478 if (GetRichTextCtrl())
8479 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8480
5d7836c4
JS
8481 success = true;
8482 }
8483 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8484 {
8485 wxBitmapDataObject data;
8486 wxTheClipboard->GetData(data);
8487 wxBitmap bitmap(data.GetBitmap());
8488 wxImage image(bitmap.ConvertToImage());
8489
603f702b 8490 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 8491
5d7836c4
JS
8492 action->GetNewParagraphs().AddImage(image);
8493
8494 if (action->GetNewParagraphs().GetChildCount() == 1)
8495 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 8496
9c8e10ad 8497 action->SetPosition(position+1);
7fe8059f 8498
5d7836c4 8499 // Set the range we'll need to delete in Undo
9c8e10ad 8500 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 8501
5d7836c4
JS
8502 SubmitAction(action);
8503
8504 success = true;
8505 }
8506 wxTheClipboard->Close();
8507 }
8508 }
39a1c2f2
WS
8509#else
8510 wxUnusedVar(position);
8511#endif
5d7836c4
JS
8512 return success;
8513}
8514
8515/// Can we paste from the clipboard?
8516bool wxRichTextBuffer::CanPasteFromClipboard() const
8517{
7fe8059f 8518 bool canPaste = false;
11ef729d 8519#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 8520 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 8521 {
f7d83f24
VZ
8522 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8523#if wxUSE_UNICODE
603f702b
JS
8524 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8525#endif
8526 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8527 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 8528 {
7fe8059f 8529 canPaste = true;
5d7836c4
JS
8530 }
8531 wxTheClipboard->Close();
8532 }
39a1c2f2 8533#endif
5d7836c4
JS
8534 return canPaste;
8535}
8536
8537/// Dumps contents of buffer for debugging purposes
8538void wxRichTextBuffer::Dump()
8539{
8540 wxString text;
8541 {
8542 wxStringOutputStream stream(& text);
8543 wxTextOutputStream textStream(stream);
8544 Dump(textStream);
8545 }
8546
8547 wxLogDebug(text);
8548}
8549
d2d0adc7
JS
8550/// Add an event handler
8551bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8552{
8553 m_eventHandlers.Append(handler);
8554 return true;
8555}
8556
8557/// Remove an event handler
8558bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8559{
8560 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8561 if (node)
8562 {
8563 m_eventHandlers.Erase(node);
8564 if (deleteHandler)
8565 delete handler;
3e541562 8566
d2d0adc7
JS
8567 return true;
8568 }
8569 else
8570 return false;
8571}
8572
8573/// Clear event handlers
8574void wxRichTextBuffer::ClearEventHandlers()
8575{
8576 m_eventHandlers.Clear();
8577}
8578
8579/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8580/// otherwise will stop at the first successful one.
8581bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8582{
8583 bool success = false;
8584 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8585 {
8586 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8587 if (handler->ProcessEvent(event))
8588 {
8589 success = true;
8590 if (!sendToAll)
8591 return true;
8592 }
8593 }
8594 return success;
8595}
8596
8597/// Set style sheet and notify of the change
8598bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8599{
8600 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 8601
616c7cbd 8602 wxWindowID winid = wxID_ANY;
d2d0adc7 8603 if (GetRichTextCtrl())
616c7cbd 8604 winid = GetRichTextCtrl()->GetId();
3e541562 8605
ce7fe42e 8606 wxRichTextEvent event(wxEVT_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 8607 event.SetEventObject(GetRichTextCtrl());
603f702b 8608 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
8609 event.SetOldStyleSheet(oldSheet);
8610 event.SetNewStyleSheet(sheet);
8611 event.Allow();
3e541562 8612
d2d0adc7
JS
8613 if (SendEvent(event) && !event.IsAllowed())
8614 {
8615 if (sheet != oldSheet)
8616 delete sheet;
8617
8618 return false;
8619 }
8620
8621 if (oldSheet && oldSheet != sheet)
8622 delete oldSheet;
8623
8624 SetStyleSheet(sheet);
8625
ce7fe42e 8626 event.SetEventType(wxEVT_RICHTEXT_STYLESHEET_REPLACED);
d2d0adc7
JS
8627 event.SetOldStyleSheet(NULL);
8628 event.Allow();
8629
8630 return SendEvent(event);
8631}
8632
8633/// Set renderer, deleting old one
8634void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8635{
8636 if (sm_renderer)
8637 delete sm_renderer;
8638 sm_renderer = renderer;
8639}
8640
603f702b
JS
8641/// Hit-testing: returns a flag indicating hit test details, plus
8642/// information about position
8db2e3ef 8643int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 8644{
8db2e3ef 8645 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
8646 if (ret != wxRICHTEXT_HITTEST_NONE)
8647 {
8648 return ret;
8649 }
8650 else
8651 {
8652 textPosition = m_ownRange.GetEnd()-1;
8653 *obj = this;
8654 *contextObj = this;
8655 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8656 }
8657}
8658
32423dd8
JS
8659void wxRichTextBuffer::SetFontScale(double fontScale)
8660{
8661 m_fontScale = fontScale;
8662 m_fontTable.SetFontScale(fontScale);
8663}
8664
8665void wxRichTextBuffer::SetDimensionScale(double dimScale)
8666{
8667 m_dimensionScale = dimScale;
8668}
8669
24777478 8670bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8671{
a1b806b9 8672 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8673 {
ecb5fbf1
JS
8674 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8675 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8676 }
8677 else
8678 {
ecb5fbf1
JS
8679 wxCheckSetPen(dc, *wxBLACK_PEN);
8680 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8681 }
8682
8683 wxFont font;
44cc96a8
JS
8684 if (bulletAttr.HasFont())
8685 {
8686 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8687 }
d2d0adc7
JS
8688 else
8689 font = (*wxNORMAL_FONT);
8690
ecb5fbf1 8691 wxCheckSetFont(dc, font);
d2d0adc7
JS
8692
8693 int charHeight = dc.GetCharHeight();
3e541562 8694
d2d0adc7
JS
8695 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8696 int bulletHeight = bulletWidth;
8697
8698 int x = rect.x;
3e541562 8699
d2d0adc7
JS
8700 // Calculate the top position of the character (as opposed to the whole line height)
8701 int y = rect.y + (rect.height - charHeight);
3e541562 8702
d2d0adc7
JS
8703 // Calculate where the bullet should be positioned
8704 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8705
d2d0adc7 8706 // The margin between a bullet and text.
44219ff0 8707 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8708
d2d0adc7
JS
8709 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8710 x = rect.x + rect.width - bulletWidth - margin;
8711 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8712 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8713
d2d0adc7
JS
8714 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8715 {
8716 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8717 }
8718 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8719 {
8720 wxPoint pts[5];
8721 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8722 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8723 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8724 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8725
d2d0adc7
JS
8726 dc.DrawPolygon(4, pts);
8727 }
8728 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8729 {
8730 wxPoint pts[3];
8731 pts[0].x = x; pts[0].y = y;
8732 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8733 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8734
d2d0adc7
JS
8735 dc.DrawPolygon(3, pts);
8736 }
603f702b
JS
8737 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8738 {
8739 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8740 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8741 }
d2d0adc7
JS
8742 else // "standard/circle", and catch-all
8743 {
8744 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8745 }
8746
d2d0adc7
JS
8747 return true;
8748}
8749
24777478 8750bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8751{
8752 if (!text.empty())
8753 {
8754 wxFont font;
44cc96a8
JS
8755 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8756 {
24777478 8757 wxRichTextAttr fontAttr;
32423dd8
JS
8758 if (attr.HasFontPixelSize())
8759 fontAttr.SetFontPixelSize(attr.GetFontSize());
8760 else
8761 fontAttr.SetFontPointSize(attr.GetFontSize());
44cc96a8
JS
8762 fontAttr.SetFontStyle(attr.GetFontStyle());
8763 fontAttr.SetFontWeight(attr.GetFontWeight());
8764 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8765 fontAttr.SetFontFaceName(attr.GetBulletFont());
8766 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8767 }
8768 else if (attr.HasFont())
8769 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8770 else
8771 font = (*wxNORMAL_FONT);
8772
ecb5fbf1 8773 wxCheckSetFont(dc, font);
d2d0adc7 8774
a1b806b9 8775 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8776 dc.SetTextForeground(attr.GetTextColour());
8777
04ee05f9 8778 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8779
8780 int charHeight = dc.GetCharHeight();
8781 wxCoord tw, th;
8782 dc.GetTextExtent(text, & tw, & th);
8783
8784 int x = rect.x;
8785
8786 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8787 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8788
8789 // The margin between a bullet and text.
44219ff0 8790 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8791
d2d0adc7
JS
8792 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8793 x = (rect.x + rect.width) - tw - margin;
8794 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8795 x = x + (rect.width)/2 - tw/2;
8796
8797 dc.DrawText(text, x, y);
3e541562 8798
d2d0adc7
JS
8799 return true;
8800 }
8801 else
8802 return false;
8803}
8804
24777478 8805bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8806{
8807 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8808 // with the buffer. The store will allow retrieval from memory, disk or other means.
8809 return false;
8810}
8811
8812/// Enumerate the standard bullet names currently supported
8813bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8814{
04529b2a 8815 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8816 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8817 bulletNames.Add(wxTRANSLATE("standard/square"));
8818 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8819 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8820
8821 return true;
8822}
5d7836c4 8823
bec80f4f
JS
8824/*!
8825 * wxRichTextBox
8826 */
8827
603f702b 8828IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8829
8830wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8831 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8832{
8833}
8834
8835/// Draw the item
8db2e3ef 8836bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8837{
603f702b
JS
8838 if (!IsShown())
8839 return true;
5ad9ae3a 8840
603f702b
JS
8841 // TODO: if the active object in the control, draw an indication.
8842 // We need to add the concept of active object, and not just focus object,
8843 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8844 // Ultimately we would like to be able to interactively resize an active object
8845 // using drag handles.
8db2e3ef 8846 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8847}
5ad9ae3a 8848
603f702b
JS
8849/// Copy
8850void wxRichTextBox::Copy(const wxRichTextBox& obj)
8851{
8852 wxRichTextParagraphLayoutBox::Copy(obj);
8853}
8854
8855// Edit properties via a GUI
8856bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8857{
8858 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8859 boxDlg.SetAttributes(GetAttributes());
8860
8861 if (boxDlg.ShowModal() == wxID_OK)
8862 {
8863 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8864 // indeterminate in the object.
8865 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8866 return true;
5ad9ae3a 8867 }
603f702b
JS
8868 else
8869 return false;
bec80f4f
JS
8870}
8871
7c9fdebe
JS
8872/*!
8873 * wxRichTextField
8874 */
8875
8876IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8877
8878wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8879 wxRichTextParagraphLayoutBox(parent)
8880{
8881 SetFieldType(fieldType);
8882}
8883
8884/// Draw the item
8885bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8886{
8887 if (!IsShown())
8888 return true;
8889
8890 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8891 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8892 return true;
8893
8894 // Fallback; but don't draw guidelines.
8895 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8896 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8897}
8898
8899bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8900{
8901 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8902 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8903 return true;
8904
8905 // Fallback
8906 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8907}
8908
914a4e23 8909bool 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
8910{
8911 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8912 if (fieldType)
914a4e23 8913 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
7c9fdebe 8914
914a4e23 8915 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
7c9fdebe
JS
8916}
8917
8918/// Calculate range
8919void wxRichTextField::CalculateRange(long start, long& end)
8920{
8921 if (IsTopLevel())
8922 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8923 else
8924 wxRichTextObject::CalculateRange(start, end);
8925}
8926
8927/// Copy
8928void wxRichTextField::Copy(const wxRichTextField& obj)
8929{
8930 wxRichTextParagraphLayoutBox::Copy(obj);
8931
32423dd8 8932 UpdateField(GetBuffer());
7c9fdebe
JS
8933}
8934
8935// Edit properties via a GUI
8936bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8937{
8938 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8939 if (fieldType)
8940 return fieldType->EditProperties(this, parent, buffer);
8941
8942 return false;
8943}
8944
8945bool wxRichTextField::CanEditProperties() const
8946{
8947 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8948 if (fieldType)
8949 return fieldType->CanEditProperties((wxRichTextField*) this);
8950
8951 return false;
8952}
8953
8954wxString wxRichTextField::GetPropertiesMenuLabel() const
8955{
8956 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8957 if (fieldType)
8958 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8959
8960 return wxEmptyString;
8961}
8962
32423dd8 8963bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
7c9fdebe
JS
8964{
8965 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8966 if (fieldType)
32423dd8 8967 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
7c9fdebe
JS
8968
8969 return false;
8970}
8971
8972bool wxRichTextField::IsTopLevel() const
8973{
8974 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8975 if (fieldType)
8976 return fieldType->IsTopLevel((wxRichTextField*) this);
8977
8978 return true;
8979}
8980
8981IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8982
8983IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8984
8985wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8986{
8987 Init();
8988
8989 SetName(name);
8990 SetLabel(label);
8991 SetDisplayStyle(displayStyle);
8992}
8993
8994wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8995{
8996 Init();
8997
8998 SetName(name);
8999 SetBitmap(bitmap);
9000 SetDisplayStyle(displayStyle);
9001}
9002
9003void wxRichTextFieldTypeStandard::Init()
9004{
9005 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
9006 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
9007 m_textColour = *wxWHITE;
9008 m_borderColour = *wxBLACK;
9009 m_backgroundColour = *wxBLACK;
9010 m_verticalPadding = 1;
9011 m_horizontalPadding = 3;
9012 m_horizontalMargin = 2;
9013 m_verticalMargin = 0;
9014}
9015
9016void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
9017{
9018 wxRichTextFieldType::Copy(field);
9019
9020 m_label = field.m_label;
9021 m_displayStyle = field.m_displayStyle;
9022 m_font = field.m_font;
9023 m_textColour = field.m_textColour;
9024 m_borderColour = field.m_borderColour;
9025 m_backgroundColour = field.m_backgroundColour;
9026 m_verticalPadding = field.m_verticalPadding;
9027 m_horizontalPadding = field.m_horizontalPadding;
9028 m_horizontalMargin = field.m_horizontalMargin;
9029 m_bitmap = field.m_bitmap;
9030}
9031
9032bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
9033{
9034 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9035 return false; // USe default composite drawing
9036 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9037 {
9038 int borderSize = 1;
9039
9040 wxPen borderPen(m_borderColour, 1, wxSOLID);
9041 wxBrush backgroundBrush(m_backgroundColour);
9042 wxColour textColour(m_textColour);
9043
9044 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9045 {
9046 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
9047 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
9048
9049 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
9050 backgroundBrush = wxBrush(highlightColour);
9051
9052 wxCheckSetBrush(dc, backgroundBrush);
9053 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9054 dc.DrawRectangle(rect);
9055 }
9056
9057 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9058 borderSize = 0;
9059
9060 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9061 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9062 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9063
9064 // clientArea is where the text is actually written
9065 wxRect clientArea = objectRect;
9066
9067 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9068 {
9069 dc.SetPen(borderPen);
9070 dc.SetBrush(backgroundBrush);
9071 dc.DrawRoundedRectangle(objectRect, 4.0);
9072 }
9073 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9074 {
9075 int arrowLength = objectRect.height/2;
9076 clientArea.width -= (arrowLength - m_horizontalPadding);
9077
9078 wxPoint pts[5];
9079 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9080 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9081 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9082 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9083 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9084 dc.SetPen(borderPen);
9085 dc.SetBrush(backgroundBrush);
9086 dc.DrawPolygon(5, pts);
9087 }
9088 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9089 {
9090 int arrowLength = objectRect.height/2;
9091 clientArea.width -= (arrowLength - m_horizontalPadding);
9092 clientArea.x += (arrowLength - m_horizontalPadding);
9093
9094 wxPoint pts[5];
9095 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9096 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9097 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9098 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9099 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9100 dc.SetPen(borderPen);
9101 dc.SetBrush(backgroundBrush);
9102 dc.DrawPolygon(5, pts);
9103 }
9104
9105 if (m_bitmap.IsOk())
9106 {
9107 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9108 int y = clientArea.y + m_verticalPadding;
9109 dc.DrawBitmap(m_bitmap, x, y, true);
9110
9111 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9112 {
9113 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9114 wxCheckSetPen(dc, *wxBLACK_PEN);
9115 dc.SetLogicalFunction(wxINVERT);
9116 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9117 dc.SetLogicalFunction(wxCOPY);
9118 }
9119 }
9120 else
9121 {
9122 wxString label(m_label);
9123 if (label.IsEmpty())
9124 label = wxT("??");
9125 int w, h, maxDescent;
9126 dc.SetFont(m_font);
9127 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9128 dc.SetTextForeground(textColour);
9129
9130 int x = clientArea.x + (clientArea.width - w)/2;
9131 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9132 dc.DrawText(m_label, x, y);
9133 }
9134 }
9135
9136 return true;
9137}
9138
9139bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9140{
9141 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9142 return false; // USe default composite layout
9143
9144 wxSize size = GetSize(obj, dc, context, style);
9145 obj->SetCachedSize(size);
9146 obj->SetMinSize(size);
9147 obj->SetMaxSize(size);
9148 return true;
9149}
9150
914a4e23 9151bool 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
9152{
9153 if (IsTopLevel(obj))
914a4e23 9154 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
7c9fdebe
JS
9155 else
9156 {
9157 wxSize sz = GetSize(obj, dc, context, 0);
9158 if (partialExtents)
9159 {
9160 int lastSize;
9161 if (partialExtents->GetCount() > 0)
9162 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9163 else
9164 lastSize = 0;
9165 partialExtents->Add(lastSize + sz.x);
9166 }
9167 size = sz;
9168 return true;
9169 }
9170}
9171
9172wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9173{
9174 int borderSize = 1;
9175 int w = 0, h = 0, maxDescent = 0;
9176
9177 wxSize sz;
9178 if (m_bitmap.IsOk())
9179 {
9180 w = m_bitmap.GetWidth();
9181 h = m_bitmap.GetHeight();
9182
9183 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9184 }
9185 else
9186 {
9187 wxString label(m_label);
9188 if (label.IsEmpty())
9189 label = wxT("??");
9190 dc.SetFont(m_font);
9191 dc.GetTextExtent(label, & w, &h, & maxDescent);
9192
9193 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9194 }
9195
9196 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9197 {
9198 sz.x += borderSize*2;
9199 sz.y += borderSize*2;
9200 }
9201
9202 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9203 {
9204 // Add space for the arrow
9205 sz.x += (sz.y/2 - m_horizontalPadding);
9206 }
9207
9208 return sz;
9209}
9210
603f702b
JS
9211IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9212
9213wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9214 wxRichTextBox(parent)
bec80f4f 9215{
603f702b
JS
9216}
9217
9218/// Draw the item
8db2e3ef 9219bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9220{
8db2e3ef 9221 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
9222}
9223
9224/// Copy
9225void wxRichTextCell::Copy(const wxRichTextCell& obj)
9226{
9227 wxRichTextBox::Copy(obj);
9228}
9229
9230// Edit properties via a GUI
9231bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9232{
9233 // We need to gather common attributes for all selected cells.
9234
9235 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9236 bool multipleCells = false;
9237 wxRichTextAttr attr;
9238
9239 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9240 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 9241 {
603f702b
JS
9242 wxRichTextAttr clashingAttr, absentAttr;
9243 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9244 size_t i;
9245 int selectedCellCount = 0;
9246 for (i = 0; i < sel.GetCount(); i++)
9247 {
9248 const wxRichTextRange& range = sel[i];
9249 wxRichTextCell* cell = table->GetCell(range.GetStart());
9250 if (cell)
9251 {
9252 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 9253
603f702b
JS
9254 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9255
9256 selectedCellCount ++;
9257 }
9258 }
9259 multipleCells = selectedCellCount > 1;
5ad9ae3a 9260 }
603f702b
JS
9261 else
9262 {
9263 attr = GetAttributes();
9264 }
9265
9266 wxString caption;
9267 if (multipleCells)
9268 caption = _("Multiple Cell Properties");
9269 else
9270 caption = _("Cell Properties");
9271
9272 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9273 cellDlg.SetAttributes(attr);
9274
80a46597 9275 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
603f702b
JS
9276 if (sizePage)
9277 {
9278 // We don't want position and floating controls for a cell.
9279 sizePage->ShowPositionControls(false);
9280 sizePage->ShowFloatingControls(false);
9281 }
9282
9283 if (cellDlg.ShowModal() == wxID_OK)
9284 {
9285 if (multipleCells)
9286 {
9287 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9288 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9289 // since it may represent clashing attributes across multiple objects.
9290 table->SetCellStyle(sel, attr);
9291 }
9292 else
9293 // For a single object, indeterminate attributes set by the user should be reflected in the
9294 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9295 // the style directly instead of applying (which ignores indeterminate attributes,
9296 // leaving them as they were).
9297 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9298 return true;
9299 }
9300 else
9301 return false;
9302}
9303
8a28cd66
JS
9304// The next 2 methods return span values. Note that the default is 1, not 0
9305int wxRichTextCell::GetColspan() const
9306{
9307 int span = 1;
9308 if (GetProperties().HasProperty(wxT("colspan")))
9309 {
9310 span = GetProperties().GetPropertyLong(wxT("colspan"));
9311 }
9312
9313 return span;
9314}
9315
9316int wxRichTextCell::GetRowspan() const
9317{
9318 int span = 1;
9319 if (GetProperties().HasProperty(wxT("rowspan")))
9320 {
9321 span = GetProperties().GetPropertyLong(wxT("rowspan"));
9322 }
9323
9324 return span;
9325}
9326
603f702b
JS
9327WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9328
9329IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9330
9331wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9332{
9333 m_rowCount = 0;
9334 m_colCount = 0;
9335}
5ad9ae3a 9336
603f702b 9337// Draws the object.
8db2e3ef 9338bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9339{
8db2e3ef 9340 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
9341}
9342
603f702b
JS
9343WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9344WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9345
8e77fd8b
JS
9346
9347 // Helper function for Layout() that clears the space needed by a cell with rowspan > 1
9348int GetRowspanDisplacement(const wxRichTextTable* table, int row, int col, int paddingX, const wxArrayInt& colWidths)
9349{
9350 // If one or more cells above-left of this one has rowspan > 1, the affected cells below it
9351 // will have been hidden and have width 0. As a result they are ignored by the layout algorithm,
9352 // and all cells to their right are effectively shifted left. As a result there's no hole for
9353 // the spanning cell to fill.
9354 // So search back along the current row for hidden cells. However there's also the annoying issue of a
9355 // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above
9356 // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span;
9357 // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan.
9358 // The answer is to look above each hidden cell in turn, which I think covers all bases.
9359 int deltaX = 0;
9360 for (int prevcol = 0; prevcol < col; ++prevcol)
9361 {
9362 if (!table->GetCell(row, prevcol)->IsShown())
9363 {
9364 // We've found a hidden cell. If it's hidden because of colspan to its left, it's
9365 // already been taken into account; but not if there's a rowspanning cell above
9366 for (int prevrow = row-1; prevrow >= 0; --prevrow)
9367 {
9368 wxRichTextCell* cell = table->GetCell(prevrow, prevcol);
9369 if (cell && cell->IsShown())
9370 {
9371 int rowSpan = cell->GetRowspan();
9372 if (rowSpan > 1 && rowSpan > (row-prevrow))
9373 {
9374 // There is a rowspanning cell above above the hidden one, so we need
9375 // to right-shift the index cell by this column's width. Furthermore,
9376 // if the cell also colspans, we need to shift by all affected columns
9377 for (int colSpan = 0; colSpan < cell->GetColspan(); ++colSpan)
9378 deltaX += (colWidths[prevcol+colSpan] + paddingX);
9379 break;
9380 }
9381 }
9382 }
9383 }
9384 }
9385 return deltaX;
9386}
9387
9388 // Helper function for Layout() that expands any cell with rowspan > 1
9389void ExpandCellsWithRowspan(const wxRichTextTable* table, int paddingY, int& bottomY, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& availableSpace, int style)
9390{
9391 // This is called when the table's cell layout is otherwise complete.
9392 // For any cell with rowspan > 1, expand downwards into the row(s) below.
9393
9394 // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells.
9395 // 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.)
9396 const int rowCount = table->GetRowCount();
9397 const int colCount = table->GetColumnCount();
9398 wxArrayInt rowTops;
9399 rowTops.Add(0, rowCount+1);
9400 for (int row = 0; row < rowCount; ++row)
9401 {
9402 for (int column = 0; column < colCount; ++column)
9403 {
9404 wxRichTextCell* cell = table->GetCell(row, column);
9405 if (cell && cell->IsShown())
9406 {
9407 rowTops[row] = cell->GetPosition().y;
9408 break;
9409 }
9410 }
9411 }
9412 rowTops[rowCount] = bottomY + paddingY; // The table bottom, which was passed to us
9413
9414 bool needsRelay = false;
9415
9416 int row, col;
9417 for (row = 0; row < rowCount-1; ++row) // -1 as the bottom row can't rowspan
9418 {
9419 for (col = 0; col < colCount; ++col)
9420 {
9421 wxRichTextCell* cell = table->GetCell(row, col);
9422 if (cell && cell->IsShown())
9423 {
9424 int span = cell->GetRowspan();
9425 if (span > 1)
9426 {
9427 span = wxMin(span, rowCount-row); // Don't try to span below the table!
9428 if (span < 2)
9429 continue;
9430
9431 int availableHeight = rowTops[row+span] - rowTops[row] - paddingY;
9432 wxSize newSize = wxSize(cell->GetCachedSize().GetWidth(), availableHeight);
9433 wxRect availableCellSpace = wxRect(cell->GetPosition(), newSize);
9434 cell->Invalidate(wxRICHTEXT_ALL);
9435 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9436 // Ensure there's room in the span to display its contents, else it'll overwrite lower rows
9437 int overhang = cell->GetCachedSize().GetHeight() - availableHeight;
9438 cell->SetCachedSize(newSize);
9439
9440 if (overhang > 0)
9441 {
9442 // There are 3 things to get right:
9443 // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself
9444 // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang
9445 // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang.
9446 // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later.
9447 int deltaY = overhang / span;
9448 int spare = overhang % span;
9449
9450 // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare).
9451 // This is achieved by increasing the value stored in the following row's rowTops
9452 for (int spannedRows = 0; spannedRows < span; ++spannedRows)
9453 {
9454 rowTops[row+spannedRows+1] += ((deltaY * (spannedRows+1)) + (spannedRows == 0 ? spare:0));
9455 }
9456
9457 // Any rows below the span need shifting down
9458 for (int rowsBelow = row + span+1; rowsBelow <= rowCount; ++rowsBelow)
9459 {
9460 rowTops[rowsBelow] += overhang;
9461 }
9462
9463 needsRelay = true;
9464 }
9465 }
9466 }
9467 }
9468 }
9469
9470 if (!needsRelay)
9471 return;
9472
9473 // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show
9474 for (row = 0; row < rowCount; ++row)
9475 {
9476 for (col = 0; col < colCount; ++col)
9477 {
9478 wxRichTextCell* cell = table->GetCell(row, col);
9479 if (cell && cell->IsShown())
9480 {
9481 wxPoint position(cell->GetPosition().x, rowTops[row]);
9482
9483 // GetRowspan() will usually return 1, but may be greater
9484 wxSize size(cell->GetCachedSize().GetWidth(), rowTops[row + cell->GetRowspan()] - rowTops[row] - paddingY);
9485
9486 wxRect availableCellSpace = wxRect(position, size);
9487 cell->Invalidate(wxRICHTEXT_ALL);
9488 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9489 cell->SetCachedSize(size);
9490 }
9491 }
9492
9493 bottomY = rowTops[rowCount] - paddingY;
9494 }
9495}
9496
603f702b
JS
9497// Lays the object out. rect is the space available for layout. Often it will
9498// be the specified overall space for this object, if trying to constrain
9499// layout to a particular size, or it could be the total space available in the
9500// parent. rect is the overall size, so we must subtract margins and padding.
9501// to get the actual available space.
8db2e3ef 9502bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 9503{
603f702b
JS
9504 SetPosition(rect.GetPosition());
9505
8a28cd66 9506 // The meaty bit. Calculate sizes of all cells and rows. Try to use
603f702b
JS
9507 // minimum size if within alloted size, then divide up remaining size
9508 // between rows/cols.
9509
9510 double scale = 1.0;
9511 wxRichTextBuffer* buffer = GetBuffer();
9512 if (buffer) scale = buffer->GetScale();
9513
8db2e3ef 9514 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
9515 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9516
8db2e3ef
JS
9517 wxRichTextAttr attr(GetAttributes());
9518 context.ApplyVirtualAttributes(attr, this);
9519
603f702b
JS
9520 // If we have no fixed table size, and assuming we're not pushed for
9521 // space, then we don't have to try to stretch the table to fit the contents.
9522 bool stretchToFitTableWidth = false;
9523
9524 int tableWidth = rect.width;
8db2e3ef 9525 if (attr.GetTextBoxAttr().GetWidth().IsValid())
603f702b 9526 {
8db2e3ef 9527 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
9528
9529 // Fixed table width, so we do want to stretch columns out if necessary.
9530 stretchToFitTableWidth = true;
9531
9532 // Shouldn't be able to exceed the size passed to this function
9533 tableWidth = wxMin(rect.width, tableWidth);
9534 }
9535
9536 // Get internal padding
36307fdf 9537 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
9538 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9539 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9540 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9541 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
9542
9543 // Assume that left and top padding are also used for inter-cell padding.
9544 int paddingX = paddingLeft;
9545 int paddingY = paddingTop;
9546
9547 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 9548 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
9549
9550 // Internal table width - the area for content
9551 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9552
9553 int rowCount = m_cells.GetCount();
9554 if (m_colCount == 0 || rowCount == 0)
9555 {
9556 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9557 SetCachedSize(overallRect.GetSize());
9558
9559 // Zero content size
9560 SetMinSize(overallRect.GetSize());
9561 SetMaxSize(GetMinSize());
9562 return true;
9563 }
9564
9565 // The final calculated widths
bb7bbd12
JS
9566 wxArrayInt colWidths;
9567 colWidths.Add(0, m_colCount);
603f702b 9568
bb7bbd12
JS
9569 wxArrayInt absoluteColWidths;
9570 absoluteColWidths.Add(0, m_colCount);
7c9fdebe 9571
bb7bbd12
JS
9572 wxArrayInt percentageColWidths;
9573 percentageColWidths.Add(0, m_colCount);
603f702b
JS
9574 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9575 // These are only relevant when the first column contains spanning information.
9576 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
9577 wxArrayInt maxColWidths;
9578 maxColWidths.Add(0, m_colCount);
9579 wxArrayInt minColWidths;
9580 minColWidths.Add(0, m_colCount);
603f702b
JS
9581
9582 wxSize tableSize(tableWidth, 0);
9583
9584 int i, j, k;
9585
9586 for (i = 0; i < m_colCount; i++)
9587 {
9588 absoluteColWidths[i] = 0;
9589 // absoluteColWidthsSpanning[i] = 0;
9590 percentageColWidths[i] = -1;
9591 // percentageColWidthsSpanning[i] = -1;
9592 colWidths[i] = 0;
9593 maxColWidths[i] = 0;
9594 minColWidths[i] = 0;
9595 // columnSpans[i] = 1;
9596 }
9597
9598 // (0) Determine which cells are visible according to spans
9599 // 1 2 3 4 5
9600 // __________________
9601 // | | | | | 1
9602 // |------| |----|
9603 // |------| | | 2
9604 // |------| | | 3
9605 // |------------------|
9606 // |__________________| 4
9607
9608 // To calculate cell visibility:
9609 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9610 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9611 // that cell, hide the cell.
9612
9613 // We can also use this array to match the size of spanning cells to the grid. Or just do
9614 // this when we iterate through all cells.
9615
9616 // 0.1: add spanning cells to an array
9617 wxRichTextRectArray rectArray;
9618 for (j = 0; j < m_rowCount; j++)
9619 {
9620 for (i = 0; i < m_colCount; i++)
9621 {
8a28cd66
JS
9622 wxRichTextCell* cell = GetCell(j, i);
9623 int colSpan = cell->GetColspan();
9624 int rowSpan = cell->GetRowspan();
603f702b
JS
9625 if (colSpan > 1 || rowSpan > 1)
9626 {
9627 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9628 }
9629 }
9630 }
9631 // 0.2: find which cells are subsumed by a spanning cell
9632 for (j = 0; j < m_rowCount; j++)
9633 {
9634 for (i = 0; i < m_colCount; i++)
9635 {
8a28cd66 9636 wxRichTextCell* cell = GetCell(j, i);
603f702b
JS
9637 if (rectArray.GetCount() == 0)
9638 {
9639 cell->Show(true);
9640 }
9641 else
9642 {
8a28cd66
JS
9643 int colSpan = cell->GetColspan();
9644 int rowSpan = cell->GetRowspan();
9645
603f702b
JS
9646 if (colSpan > 1 || rowSpan > 1)
9647 {
9648 // Assume all spanning cells are shown
9649 cell->Show(true);
9650 }
9651 else
9652 {
9653 bool shown = true;
9654 for (k = 0; k < (int) rectArray.GetCount(); k++)
9655 {
9656 if (rectArray[k].Contains(wxPoint(i, j)))
9657 {
9658 shown = false;
9659 break;
9660 }
9661 }
9662 cell->Show(shown);
9663 }
9664 }
9665 }
9666 }
9667
8a28cd66 9668 // Find the first spanned cell in each row that spans the most columns and doesn't
603f702b
JS
9669 // overlap with a spanned cell starting at a previous column position.
9670 // This means we need to keep an array of rects so we can check. However
9671 // it does also mean that some spans simply may not be taken into account
9672 // where there are different spans happening on different rows. In these cases,
9673 // they will simply be as wide as their constituent columns.
9674
9675 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9676 // the absolute or percentage width of each column.
9677
9678 for (j = 0; j < m_rowCount; j++)
9679 {
9680 // First get the overall margins so we can calculate percentage widths based on
9681 // the available content space for all cells on the row
9682
9683 int overallRowContentMargin = 0;
9684 int visibleCellCount = 0;
9685
9686 for (i = 0; i < m_colCount; i++)
9687 {
9688 wxRichTextBox* cell = GetCell(j, i);
9689 if (cell->IsShown())
9690 {
9691 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9692 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9693
9694 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9695 visibleCellCount ++;
9696 }
9697 }
9698
9699 // Add in inter-cell padding
9700 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9701
9702 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9703 wxSize rowTableSize(rowContentWidth, 0);
9704 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9705
9706 for (i = 0; i < m_colCount; i++)
9707 {
8a28cd66 9708 wxRichTextCell* cell = GetCell(j, i);
603f702b
JS
9709 if (cell->IsShown())
9710 {
8a28cd66 9711 int colSpan = cell->GetColspan();
603f702b
JS
9712
9713 // Lay out cell to find min/max widths
9714 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9715 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
9716
9717 if (colSpan == 1)
9718 {
9719 int absoluteCellWidth = -1;
9720 int percentageCellWidth = -1;
9721
9722 // I think we need to calculate percentages from the internal table size,
9723 // minus the padding between cells which we'll need to calculate from the
9724 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9725 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9726 // so if we want to conform to that we'll need to add in the overall cell margins.
9727 // However, this will make it difficult to specify percentages that add up to
9728 // 100% and still fit within the table width.
9729 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9730 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9731 // If we're using internal content size for the width, we would calculate the
9732 // the overall cell width for n cells as:
9733 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9734 // + thisOverallCellMargin
9735 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9736 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9737
9738 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9739 {
9740 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9741 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9742 {
9743 percentageCellWidth = w;
9744 }
9745 else
9746 {
9747 absoluteCellWidth = w;
9748 }
9749 // Override absolute width with minimum width if necessary
9750 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9751 absoluteCellWidth = cell->GetMinSize().x;
9752 }
9753
9754 if (absoluteCellWidth != -1)
9755 {
9756 if (absoluteCellWidth > absoluteColWidths[i])
9757 absoluteColWidths[i] = absoluteCellWidth;
9758 }
9759
9760 if (percentageCellWidth != -1)
9761 {
9762 if (percentageCellWidth > percentageColWidths[i])
9763 percentageColWidths[i] = percentageCellWidth;
9764 }
9765
9766 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9767 minColWidths[i] = cell->GetMinSize().x;
9768 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9769 maxColWidths[i] = cell->GetMaxSize().x;
9770 }
9771 }
9772 }
9773 }
9774
9775 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9776 // TODO: simply merge this into (1).
9777 for (i = 0; i < m_colCount; i++)
9778 {
9779 if (absoluteColWidths[i] > 0)
9780 {
9781 colWidths[i] = absoluteColWidths[i];
9782 }
9783 else if (percentageColWidths[i] > 0)
9784 {
9785 colWidths[i] = percentageColWidths[i];
9786
9787 // This is rubbish - we calculated the absolute widths from percentages, so
9788 // we can't do it again here.
9789 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9790 }
9791 }
9792
9793 // (3) Process absolute or proportional widths of spanning columns,
9794 // now that we know what our fixed column widths are going to be.
9795 // Spanned cells will try to adjust columns so the span will fit.
9796 // Even existing fixed column widths can be expanded if necessary.
9797 // Actually, currently fixed columns widths aren't adjusted; instead,
9798 // the algorithm favours earlier rows and adjusts unspecified column widths
9799 // the first time only. After that, we can't know whether the column has been
9800 // specified explicitly or not. (We could make a note if necessary.)
9801 for (j = 0; j < m_rowCount; j++)
9802 {
9803 // First get the overall margins so we can calculate percentage widths based on
9804 // the available content space for all cells on the row
9805
9806 int overallRowContentMargin = 0;
9807 int visibleCellCount = 0;
9808
9809 for (i = 0; i < m_colCount; i++)
9810 {
9811 wxRichTextBox* cell = GetCell(j, i);
9812 if (cell->IsShown())
9813 {
9814 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9815 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9816
9817 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9818 visibleCellCount ++;
9819 }
9820 }
9821
9822 // Add in inter-cell padding
9823 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9824
9825 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9826 wxSize rowTableSize(rowContentWidth, 0);
9827 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9828
9829 for (i = 0; i < m_colCount; i++)
9830 {
8a28cd66 9831 wxRichTextCell* cell = GetCell(j, i);
603f702b
JS
9832 if (cell->IsShown())
9833 {
8a28cd66 9834 int colSpan = cell->GetColspan();
603f702b
JS
9835 if (colSpan > 1)
9836 {
9837 int spans = wxMin(colSpan, m_colCount - i);
9838 int cellWidth = 0;
9839 if (spans > 0)
9840 {
9841 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9842 {
9843 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9844 // Override absolute width with minimum width if necessary
9845 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9846 cellWidth = cell->GetMinSize().x;
9847 }
9848 else
9849 {
9850 // Do we want to do this? It's the only chance we get to
9851 // use the cell's min/max sizes, so we need to work out
9852 // how we're going to balance the unspecified spanning cell
9853 // width with the possibility more-constrained constituent cell widths.
9854 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9855 // don't want to constraint all the spanned columns to fit into this cell.
9856 // OK, let's say that if any of the constituent columns don't fit,
9857 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9858 // cells to the columns later.
9859 cellWidth = cell->GetMinSize().x;
9860 if (cell->GetMaxSize().x > cellWidth)
9861 cellWidth = cell->GetMaxSize().x;
9862 }
9863
9864 // Subtract the padding between cells
9865 int spanningWidth = cellWidth;
9866 spanningWidth -= paddingX * (spans-1);
9867
9868 if (spanningWidth > 0)
9869 {
9870 // Now share the spanning width between columns within that span
9871 // TODO: take into account min widths of columns within the span
9872 int spanningWidthLeft = spanningWidth;
9873 int stretchColCount = 0;
9874 for (k = i; k < (i+spans); k++)
9875 {
9876 if (colWidths[k] > 0) // absolute or proportional width has been specified
9877 spanningWidthLeft -= colWidths[k];
9878 else
9879 stretchColCount ++;
9880 }
9881 // Now divide what's left between the remaining columns
9882 int colShare = 0;
9883 if (stretchColCount > 0)
9884 colShare = spanningWidthLeft / stretchColCount;
9885 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9886
9887 // If fixed-width columns are currently too big, then we'll later
9888 // stretch the spanned cell to fit.
9889
9890 if (spanningWidthLeft > 0)
9891 {
9892 for (k = i; k < (i+spans); k++)
9893 {
9894 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9895 {
9896 int newWidth = colShare;
9897 if (k == (i+spans-1))
9898 newWidth += colShareRemainder; // ensure all pixels are filled
9899 colWidths[k] = newWidth;
9900 }
9901 }
9902 }
9903 }
9904 }
9905 }
9906 }
9907 }
9908 }
9909
9910 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9911 // TODO: take into account min widths of columns within the span
9912 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9913 int widthLeft = tableWidthMinusPadding;
9914 int stretchColCount = 0;
9915 for (i = 0; i < m_colCount; i++)
9916 {
603f702b
JS
9917 // Subtract min width from width left, then
9918 // add the colShare to the min width
9919 if (colWidths[i] > 0) // absolute or proportional width has been specified
9920 widthLeft -= colWidths[i];
9921 else
9922 {
9923 if (minColWidths[i] > 0)
9924 widthLeft -= minColWidths[i];
9925
9926 stretchColCount ++;
9927 }
9928 }
9929
9930 // Now divide what's left between the remaining columns
9931 int colShare = 0;
9932 if (stretchColCount > 0)
9933 colShare = widthLeft / stretchColCount;
9934 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9935
9936 // Check we don't have enough space, in which case shrink all columns, overriding
9937 // any absolute/proportional widths
9938 // TODO: actually we would like to divide up the shrinkage according to size.
9939 // How do we calculate the proportions that will achieve this?
9940 // Could first choose an arbitrary value for stretching cells, and then calculate
9941 // factors to multiply each width by.
9942 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9943 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9944 {
9945 colShare = tableWidthMinusPadding / m_colCount;
9946 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9947 for (i = 0; i < m_colCount; i++)
9948 {
9949 colWidths[i] = 0;
9950 minColWidths[i] = 0;
9951 }
9952 }
9953
9954 // We have to adjust the columns if either we need to shrink the
9955 // table to fit the parent/table width, or we explicitly set the
9956 // table width and need to stretch out the table.
9957 if (widthLeft < 0 || stretchToFitTableWidth)
9958 {
9959 for (i = 0; i < m_colCount; i++)
9960 {
9961 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9962 {
9963 if (minColWidths[i] > 0)
9964 colWidths[i] = minColWidths[i] + colShare;
9965 else
9966 colWidths[i] = colShare;
9967 if (i == (m_colCount-1))
9968 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9969 }
9970 }
9971 }
9972
9973 // TODO: if spanned cells have no specified or max width, make them the
9974 // as big as the columns they span. Do this for all spanned cells in all
9975 // rows, of course. Size any spanned cells left over at the end - even if they
9976 // have width > 0, make sure they're limited to the appropriate column edge.
9977
9978
9979/*
9980 Sort out confusion between content width
9981 and overall width later. For now, assume we specify overall width.
9982
9983 So, now we've laid out the table to fit into the given space
9984 and have used specified widths and minimum widths.
9985
9986 Now we need to consider how we will try to take maximum width into account.
9987
9988*/
9989
9990 // (??) TODO: take max width into account
9991
9992 // (6) Lay out all cells again with the current values
9993
9994 int maxRight = 0;
9995 int y = availableSpace.y;
9996 for (j = 0; j < m_rowCount; j++)
9997 {
9998 int x = availableSpace.x; // TODO: take into account centering etc.
9999 int maxCellHeight = 0;
10000 int maxSpecifiedCellHeight = 0;
10001
bb7bbd12
JS
10002 wxArrayInt actualWidths;
10003 actualWidths.Add(0, m_colCount);
603f702b
JS
10004
10005 wxTextAttrDimensionConverter converter(dc, scale);
10006 for (i = 0; i < m_colCount; i++)
10007 {
10008 wxRichTextCell* cell = GetCell(j, i);
10009 if (cell->IsShown())
10010 {
603f702b
JS
10011 // Get max specified cell height
10012 // Don't handle percentages for height
10013 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
10014 {
10015 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
10016 if (h > maxSpecifiedCellHeight)
10017 maxSpecifiedCellHeight = h;
10018 }
10019
10020 if (colWidths[i] > 0) // absolute or proportional width has been specified
10021 {
8a28cd66 10022 int colSpan = cell->GetColspan();
603f702b
JS
10023 wxRect availableCellSpace;
10024
8a28cd66 10025 // Take into account spans
603f702b
JS
10026 if (colSpan > 1)
10027 {
10028 // Calculate the size of this spanning cell from its constituent columns
8a28cd66 10029 int xx = 0;
603f702b 10030 int spans = wxMin(colSpan, m_colCount - i);
8a28cd66 10031 for (k = i; k < (i+spans); k++)
603f702b
JS
10032 {
10033 if (k != i)
10034 xx += paddingX;
10035 xx += colWidths[k];
10036 }
10037 availableCellSpace = wxRect(x, y, xx, -1);
10038 }
10039 else
10040 availableCellSpace = wxRect(x, y, colWidths[i], -1);
10041
10042 // Store actual width so we can force cell to be the appropriate width on the final loop
10043 actualWidths[i] = availableCellSpace.GetWidth();
10044
8e77fd8b
JS
10045 // We now need to shift right by the width of any rowspanning cells above-left of us
10046 int deltaX = GetRowspanDisplacement(this, j, i, paddingX, colWidths);
10047 availableCellSpace.SetX(availableCellSpace.GetX() + deltaX);
10048
603f702b
JS
10049 // Lay out cell
10050 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 10051 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
10052
10053 // TODO: use GetCachedSize().x to compute 'natural' size
10054
10055 x += (availableCellSpace.GetWidth() + paddingX);
8e77fd8b 10056 if ((cell->GetCachedSize().y > maxCellHeight) && (cell->GetRowspan() < 2))
603f702b
JS
10057 maxCellHeight = cell->GetCachedSize().y;
10058 }
10059 }
10060 }
10061
10062 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
10063
10064 for (i = 0; i < m_colCount; i++)
10065 {
10066 wxRichTextCell* cell = GetCell(j, i);
10067 if (cell->IsShown())
10068 {
10069 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
10070 // Lay out cell with new height
10071 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 10072 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
10073
10074 // Make sure the cell size really is the appropriate size,
10075 // not the calculated box size
10076 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
10077
10078 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
10079 }
10080 }
10081
10082 y += maxCellHeight;
10083 if (j < (m_rowCount-1))
10084 y += paddingY;
10085 }
8e77fd8b
JS
10086
10087 // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known
10088 ExpandCellsWithRowspan(this, paddingY, y, dc, context, availableSpace, style);
603f702b
JS
10089
10090 // We need to add back the margins etc.
10091 {
10092 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10093 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 10094 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10095 SetCachedSize(marginRect.GetSize());
10096 }
10097
10098 // TODO: calculate max size
10099 {
10100 SetMaxSize(GetCachedSize());
10101 }
10102
10103 // TODO: calculate min size
10104 {
10105 SetMinSize(GetCachedSize());
10106 }
10107
10108 // TODO: currently we use either a fixed table width or the parent's size.
10109 // We also want to be able to calculate the table width from its content,
10110 // whether using fixed column widths or cell content min/max width.
10111 // Probably need a boolean flag to say whether we need to stretch cells
10112 // to fit the table width, or to simply use min/max cell widths. The
10113 // trouble with this is that if cell widths are not specified, they
10114 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
10115 // Anyway, ignoring that problem, we probably need to factor layout into a function
10116 // that can can calculate the maximum unconstrained layout in case table size is
10117 // not specified. Then LayoutToBestSize() can choose to use either parent size to
10118 // constrain Layout(), or the previously-calculated max size to constraint layout.
10119
10120 return true;
10121}
10122
10123// Finds the absolute position and row height for the given character position
8db2e3ef 10124bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
10125{
10126 wxRichTextCell* child = GetCell(index+1);
10127 if (child)
10128 {
10129 // Find the position at the start of the child cell, since the table doesn't
10130 // have any caret position of its own.
8db2e3ef 10131 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
10132 }
10133 else
10134 return false;
10135}
10136
10137// Get the cell at the given character position (in the range of the table).
10138wxRichTextCell* wxRichTextTable::GetCell(long pos) const
10139{
10140 int row = 0, col = 0;
10141 if (GetCellRowColumnPosition(pos, row, col))
10142 {
10143 return GetCell(row, col);
10144 }
10145 else
10146 return NULL;
10147}
10148
10149// Get the row/column for a given character position
10150bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
10151{
10152 if (m_colCount == 0 || m_rowCount == 0)
10153 return false;
10154
10155 row = (int) (pos / m_colCount);
10156 col = pos - (row * m_colCount);
10157
10158 wxASSERT(row < m_rowCount && col < m_colCount);
10159
10160 if (row < m_rowCount && col < m_colCount)
10161 return true;
10162 else
10163 return false;
10164}
10165
10166// Calculate range, taking row/cell ordering into account instead of relying
10167// on list ordering.
10168void wxRichTextTable::CalculateRange(long start, long& end)
10169{
10170 long current = start;
10171 long lastEnd = current;
10172
10173 if (IsTopLevel())
10174 {
10175 current = 0;
10176 lastEnd = 0;
10177 }
10178
10179 int i, j;
10180 for (i = 0; i < m_rowCount; i++)
10181 {
10182 for (j = 0; j < m_colCount; j++)
10183 {
10184 wxRichTextCell* child = GetCell(i, j);
10185 if (child)
10186 {
10187 long childEnd = 0;
10188
10189 child->CalculateRange(current, childEnd);
10190
10191 lastEnd = childEnd;
10192 current = childEnd + 1;
10193 }
10194 }
10195 }
10196
10197 // A top-level object always has a range of size 1,
10198 // because its children don't count at this level.
10199 end = start;
10200 m_range.SetRange(start, start);
10201
10202 // An object with no children has zero length
10203 if (m_children.GetCount() == 0)
10204 lastEnd --;
10205 m_ownRange.SetRange(0, lastEnd);
10206}
10207
10208// Gets the range size.
914a4e23 10209bool 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 10210{
914a4e23 10211 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
603f702b
JS
10212}
10213
10214// Deletes content in the given range.
10215bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
10216{
10217 // TODO: implement deletion of cells
10218 return true;
10219}
10220
10221// Gets any text in this object for the given range.
10222wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10223{
10224 return wxRichTextBox::GetTextForRange(range);
10225}
10226
10227// Copies this object.
10228void wxRichTextTable::Copy(const wxRichTextTable& obj)
10229{
10230 wxRichTextBox::Copy(obj);
10231
10232 ClearTable();
10233
10234 m_rowCount = obj.m_rowCount;
10235 m_colCount = obj.m_colCount;
10236
10237 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10238
10239 int i, j;
10240 for (i = 0; i < m_rowCount; i++)
10241 {
10242 wxRichTextObjectPtrArray& colArray = m_cells[i];
10243 for (j = 0; j < m_colCount; j++)
10244 {
10245 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10246 AppendChild(cell);
10247
10248 colArray.Add(cell);
10249 }
10250 }
10251}
10252
10253void wxRichTextTable::ClearTable()
10254{
10255 m_cells.Clear();
10256 DeleteChildren();
0a6ec346
VZ
10257 m_rowCount = 0;
10258 m_colCount = 0;
603f702b
JS
10259}
10260
10261bool wxRichTextTable::CreateTable(int rows, int cols)
10262{
10263 ClearTable();
10264
10265 m_rowCount = rows;
10266 m_colCount = cols;
10267
10268 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10269
10270 int i, j;
10271 for (i = 0; i < rows; i++)
10272 {
10273 wxRichTextObjectPtrArray& colArray = m_cells[i];
10274 for (j = 0; j < cols; j++)
10275 {
10276 wxRichTextCell* cell = new wxRichTextCell;
10277 AppendChild(cell);
10278 cell->AddParagraph(wxEmptyString);
10279
10280 colArray.Add(cell);
10281 }
10282 }
10283
10284 return true;
10285}
10286
10287wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10288{
10289 wxASSERT(row < m_rowCount);
10290 wxASSERT(col < m_colCount);
10291
10292 if (row < m_rowCount && col < m_colCount)
10293 {
10294 wxRichTextObjectPtrArray& colArray = m_cells[row];
10295 wxRichTextObject* obj = colArray[col];
10296 return wxDynamicCast(obj, wxRichTextCell);
10297 }
10298 else
d67faa04 10299 return NULL;
603f702b
JS
10300}
10301
10302// Returns a selection object specifying the selections between start and end character positions.
10303// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10304wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10305{
10306 wxRichTextSelection selection;
10307 selection.SetContainer((wxRichTextTable*) this);
10308
10309 if (start > end)
10310 {
10311 long tmp = end;
10312 end = start;
10313 start = tmp;
10314 }
10315
10316 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10317
10318 if (end >= (m_colCount * m_rowCount))
10319 return selection;
10320
10321 // We need to find the rectangle of cells that is described by the rectangle
10322 // with start, end as the diagonal. Make sure we don't add cells that are
10323 // not currenty visible because they are overlapped by spanning cells.
10324/*
10325 --------------------------
10326 | 0 | 1 | 2 | 3 | 4 |
10327 --------------------------
10328 | 5 | 6 | 7 | 8 | 9 |
10329 --------------------------
10330 | 10 | 11 | 12 | 13 | 14 |
10331 --------------------------
10332 | 15 | 16 | 17 | 18 | 19 |
10333 --------------------------
10334
10335 Let's say we select 6 -> 18.
10336
10337 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10338 which is left and which is right.
10339
10340 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10341
10342 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10343 and (b) shown.
10344
10345
10346*/
10347
10348 int leftCol = start - m_colCount * int(start/m_colCount);
10349 int rightCol = end - m_colCount * int(end/m_colCount);
10350
10351 int topRow = int(start/m_colCount);
10352 int bottomRow = int(end/m_colCount);
10353
10354 if (leftCol > rightCol)
10355 {
10356 int tmp = rightCol;
10357 rightCol = leftCol;
10358 leftCol = tmp;
10359 }
10360
10361 if (topRow > bottomRow)
10362 {
10363 int tmp = bottomRow;
10364 bottomRow = topRow;
10365 topRow = tmp;
10366 }
10367
10368 int i, j;
10369 for (i = topRow; i <= bottomRow; i++)
10370 {
10371 for (j = leftCol; j <= rightCol; j++)
10372 {
10373 wxRichTextCell* cell = GetCell(i, j);
10374 if (cell && cell->IsShown())
10375 selection.Add(cell->GetRange());
10376 }
10377 }
10378
10379 return selection;
10380}
10381
10382// Sets the attributes for the cells specified by the selection.
10383bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10384{
10385 if (selection.GetContainer() != this)
10386 return false;
10387
10388 wxRichTextBuffer* buffer = GetBuffer();
10389 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10390 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10391
10392 if (withUndo)
10393 buffer->BeginBatchUndo(_("Set Cell Style"));
10394
10395 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10396 while (node)
10397 {
10398 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10399 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10400 SetStyle(cell, style, flags);
10401 node = node->GetNext();
10402 }
10403
10404 // Do action, or delay it until end of batch.
10405 if (withUndo)
10406 buffer->EndBatchUndo();
10407
10408 return true;
10409}
10410
4c86168d
JS
10411wxPosition wxRichTextTable::GetFocusedCell() const
10412{
10413 wxPosition position(-1, -1);
10414 const wxRichTextObject* focus = GetBuffer()->GetRichTextCtrl()->GetFocusObject();
10415
10416 for (int row = 0; row < GetRowCount(); ++row)
10417 {
10418 for (int col = 0; col < GetColumnCount(); ++col)
10419 {
10420 if (GetCell(row, col) == focus)
10421 {
10422 position.SetRow(row);
10423 position.SetCol(col);
10424 return position;
10425 }
10426 }
10427 }
10428
10429 return position;
10430}
10431
603f702b
JS
10432bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10433{
ef3f0679
JS
10434 wxASSERT((startRow + noRows) <= m_rowCount);
10435 if ((startRow + noRows) > m_rowCount)
603f702b
JS
10436 return false;
10437
4c86168d
JS
10438 wxCHECK_MSG(noRows != m_rowCount, false, "Trying to delete all the cells in a table");
10439
31be8400 10440 wxRichTextBuffer* buffer = GetBuffer();
4c86168d
JS
10441 wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
10442
10443 wxPosition position = GetFocusedCell();
10444 int focusCol = position.GetCol();
10445 int focusRow = position.GetRow();
10446 if (focusRow >= startRow && focusRow < (startRow+noRows))
10447 {
10448 // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object
10449 if ((startRow + noRows) < m_rowCount)
10450 {
10451 // There are more rows after the one(s) to be deleted, so set focus in the first of them
10452 rtc->SetFocusObject(GetCell(startRow + noRows, focusCol));
10453 }
10454 else
10455 {
10456 // Otherwise set focus in the preceding row
10457 rtc->SetFocusObject(GetCell(startRow - 1, focusCol));
10458 }
10459 }
10460
31be8400
JS
10461 wxRichTextAction* action = NULL;
10462 wxRichTextTable* clone = NULL;
4c86168d 10463 if (!rtc->SuppressingUndo())
31be8400
JS
10464 {
10465 // Create a clone containing the current state of the table. It will be used to Undo the action
10466 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10467 clone->SetParent(GetParent());
4c86168d 10468 action = new wxRichTextAction(NULL, _("Delete row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc);
31be8400
JS
10469 action->SetObject(this);
10470 action->SetPosition(GetRange().GetStart());
10471 }
10472
603f702b
JS
10473 int i, j;
10474 for (i = startRow; i < (startRow+noRows); i++)
10475 {
10476 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10477 for (j = 0; j < (int) colArray.GetCount(); j++)
10478 {
10479 wxRichTextObject* cell = colArray[j];
10480 RemoveChild(cell, true);
10481 }
10482
10483 // Keep deleting at the same position, since we move all
10484 // the others up
10485 m_cells.RemoveAt(startRow);
10486 }
10487
10488 m_rowCount = m_rowCount - noRows;
10489
4c86168d 10490 if (!rtc->SuppressingUndo())
31be8400
JS
10491 {
10492 buffer->SubmitAction(action);
10493 // Finally store the original-state clone; doing so earlier would cause various failures
10494 action->StoreObject(clone);
10495 }
10496
603f702b
JS
10497 return true;
10498}
10499
10500bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10501{
ef3f0679
JS
10502 wxASSERT((startCol + noCols) <= m_colCount);
10503 if ((startCol + noCols) > m_colCount)
603f702b
JS
10504 return false;
10505
4c86168d
JS
10506 wxCHECK_MSG(noCols != m_colCount, false, "Trying to delete all the cells in a table");
10507
31be8400 10508 wxRichTextBuffer* buffer = GetBuffer();
4c86168d
JS
10509 wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
10510
10511 wxPosition position = GetFocusedCell();
10512 int focusCol = position.GetCol();
10513 int focusRow = position.GetRow();
10514 if (focusCol >= startCol && focusCol < (startCol+noCols))
10515 {
10516 // Deleting a focused cell causes a segfault later when laying out, due to GetFocusedObject() returning an invalid object
10517 if ((startCol + noCols) < m_colCount)
10518 {
10519 // There are more columns after the one(s) to be deleted, so set focus in the first of them
10520 rtc->SetFocusObject(GetCell(focusRow, startCol + noCols));
10521 }
10522 else
10523 {
10524 // Otherwise set focus in the preceding column
10525 rtc->SetFocusObject(GetCell(focusRow, startCol - 1));
10526 }
10527 }
10528
31be8400
JS
10529 wxRichTextAction* action = NULL;
10530 wxRichTextTable* clone = NULL;
4c86168d 10531 if (!rtc->SuppressingUndo())
31be8400
JS
10532 {
10533 // Create a clone containing the current state of the table. It will be used to Undo the action
10534 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10535 clone->SetParent(GetParent());
4c86168d 10536 action = new wxRichTextAction(NULL, _("Delete column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc);
31be8400
JS
10537 action->SetObject(this);
10538 action->SetPosition(GetRange().GetStart());
10539 }
10540
603f702b
JS
10541 bool deleteRows = (noCols == m_colCount);
10542
10543 int i, j;
10544 for (i = 0; i < m_rowCount; i++)
10545 {
10546 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
ef3f0679 10547 for (j = 0; j < noCols; j++)
603f702b 10548 {
ef3f0679 10549 wxRichTextObject* cell = colArray[startCol];
603f702b 10550 RemoveChild(cell, true);
ef3f0679 10551 colArray.RemoveAt(startCol);
603f702b
JS
10552 }
10553
10554 if (deleteRows)
10555 m_cells.RemoveAt(0);
10556 }
10557
10558 if (deleteRows)
10559 m_rowCount = 0;
10560 m_colCount = m_colCount - noCols;
10561
4c86168d 10562 if (!rtc->SuppressingUndo())
31be8400
JS
10563 {
10564 buffer->SubmitAction(action);
10565 // Finally store the original-state clone; doing so earlier would cause various failures
10566 action->StoreObject(clone);
10567 }
10568
603f702b
JS
10569 return true;
10570}
10571
10572bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10573{
10574 wxASSERT(startRow <= m_rowCount);
10575 if (startRow > m_rowCount)
10576 return false;
10577
31be8400
JS
10578 wxRichTextBuffer* buffer = GetBuffer();
10579 wxRichTextAction* action = NULL;
10580 wxRichTextTable* clone = NULL;
10581 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10582 {
10583 // Create a clone containing the current state of the table. It will be used to Undo the action
10584 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10585 clone->SetParent(GetParent());
10586 action = new wxRichTextAction(NULL, _("Add row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl());
10587 action->SetObject(this);
10588 action->SetPosition(GetRange().GetStart());
10589 }
10590
603f702b
JS
10591 int i, j;
10592 for (i = 0; i < noRows; i++)
10593 {
10594 int idx;
10595 if (startRow == m_rowCount)
10596 {
10597 m_cells.Add(wxRichTextObjectPtrArray());
10598 idx = m_cells.GetCount() - 1;
10599 }
10600 else
10601 {
10602 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10603 idx = startRow+i;
10604 }
10605
10606 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10607 for (j = 0; j < m_colCount; j++)
10608 {
10609 wxRichTextCell* cell = new wxRichTextCell;
10610 cell->GetAttributes() = attr;
10611
10612 AppendChild(cell);
a9fd42cc 10613 cell->AddParagraph(wxEmptyString);
603f702b
JS
10614 colArray.Add(cell);
10615 }
10616 }
10617
10618 m_rowCount = m_rowCount + noRows;
31be8400
JS
10619
10620 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10621 {
10622 buffer->SubmitAction(action);
10623 // Finally store the original-state clone; doing so earlier would cause various failures
10624 action->StoreObject(clone);
10625 }
10626
603f702b
JS
10627 return true;
10628}
10629
10630bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10631{
10632 wxASSERT(startCol <= m_colCount);
10633 if (startCol > m_colCount)
10634 return false;
10635
31be8400
JS
10636 wxRichTextBuffer* buffer = GetBuffer();
10637 wxRichTextAction* action = NULL;
10638 wxRichTextTable* clone = NULL;
10639 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10640 {
10641 // Create a clone containing the current state of the table. It will be used to Undo the action
10642 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10643 clone->SetParent(GetParent());
10644 action = new wxRichTextAction(NULL, _("Add column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl());
10645 action->SetObject(this);
10646 action->SetPosition(GetRange().GetStart());
10647 }
10648
603f702b
JS
10649 int i, j;
10650 for (i = 0; i < m_rowCount; i++)
10651 {
10652 wxRichTextObjectPtrArray& colArray = m_cells[i];
10653 for (j = 0; j < noCols; j++)
10654 {
10655 wxRichTextCell* cell = new wxRichTextCell;
10656 cell->GetAttributes() = attr;
10657
10658 AppendChild(cell);
a9fd42cc 10659 cell->AddParagraph(wxEmptyString);
603f702b
JS
10660
10661 if (startCol == m_colCount)
10662 colArray.Add(cell);
10663 else
10664 colArray.Insert(cell, startCol+j);
10665 }
10666 }
10667
10668 m_colCount = m_colCount + noCols;
10669
31be8400
JS
10670 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10671 {
10672 buffer->SubmitAction(action);
10673 // Finally store the original-state clone; doing so earlier would cause various failures
10674 action->StoreObject(clone);
10675 }
10676
603f702b 10677 return true;
5ad9ae3a
JS
10678}
10679
603f702b
JS
10680// Edit properties via a GUI
10681bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 10682{
603f702b
JS
10683 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10684 boxDlg.SetAttributes(GetAttributes());
10685
10686 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 10687 {
603f702b
JS
10688 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10689 return true;
5ad9ae3a
JS
10690 }
10691 else
10692 return false;
bec80f4f
JS
10693}
10694
5d7836c4
JS
10695/*
10696 * Module to initialise and clean up handlers
10697 */
10698
10699class wxRichTextModule: public wxModule
10700{
10701DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10702public:
10703 wxRichTextModule() {}
cfa3b256
JS
10704 bool OnInit()
10705 {
d2d0adc7 10706 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
10707 wxRichTextBuffer::InitStandardHandlers();
10708 wxRichTextParagraph::InitDefaultTabs();
1aca9fcd
JS
10709
10710 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10711 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10712 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10713 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10714 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10715 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10716 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10717 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10718 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10719
cfa3b256 10720 return true;
47b378bd 10721 }
cfa3b256
JS
10722 void OnExit()
10723 {
10724 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 10725 wxRichTextBuffer::CleanUpDrawingHandlers();
7c9fdebe 10726 wxRichTextBuffer::CleanUpFieldTypes();
1aca9fcd 10727 wxRichTextXMLHandler::ClearNodeToClassMap();
cfa3b256
JS
10728 wxRichTextDecimalToRoman(-1);
10729 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 10730 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 10731 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 10732 }
5d7836c4
JS
10733};
10734
10735IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10736
10737
f1d6804f
RD
10738// If the richtext lib is dynamically loaded after the app has already started
10739// (such as from wxPython) then the built-in module system will not init this
10740// module. Provide this function to do it manually.
10741void wxRichTextModuleInit()
10742{
10743 wxModule* module = new wxRichTextModule;
f1d6804f 10744 wxModule::RegisterModule(module);
58d1949f 10745 wxModule::InitializeModules();
f1d6804f
RD
10746}
10747
10748
5d7836c4
JS
10749/*!
10750 * Commands for undo/redo
10751 *
10752 */
10753
10754wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 10755 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 10756{
603f702b 10757 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
10758}
10759
7fe8059f 10760wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
10761{
10762}
10763
10764wxRichTextCommand::~wxRichTextCommand()
10765{
10766 ClearActions();
10767}
10768
10769void wxRichTextCommand::AddAction(wxRichTextAction* action)
10770{
10771 if (!m_actions.Member(action))
10772 m_actions.Append(action);
10773}
10774
10775bool wxRichTextCommand::Do()
10776{
09f14108 10777 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
10778 {
10779 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10780 action->Do();
10781 }
10782
10783 return true;
10784}
10785
10786bool wxRichTextCommand::Undo()
10787{
09f14108 10788 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
10789 {
10790 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10791 action->Undo();
10792 }
10793
10794 return true;
10795}
10796
10797void wxRichTextCommand::ClearActions()
10798{
10799 WX_CLEAR_LIST(wxList, m_actions);
10800}
10801
10802/*!
10803 * Individual action
10804 *
10805 */
10806
603f702b
JS
10807wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10808 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10809 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
10810{
10811 m_buffer = buffer;
603f702b
JS
10812 m_object = NULL;
10813 m_containerAddress.Create(buffer, container);
5d7836c4
JS
10814 m_ignoreThis = ignoreFirstTime;
10815 m_cmdId = id;
10816 m_position = -1;
10817 m_ctrl = ctrl;
10818 m_name = name;
10819 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10820 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10821 if (cmd)
10822 cmd->AddAction(this);
10823}
10824
10825wxRichTextAction::~wxRichTextAction()
10826{
603f702b
JS
10827 if (m_object)
10828 delete m_object;
10829}
10830
10831// Returns the container that this action refers to, using the container address and top-level buffer.
10832wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10833{
10834 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10835 return container;
5d7836c4
JS
10836}
10837
603f702b 10838
7051fa41
JS
10839void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10840{
10841 // Store a list of line start character and y positions so we can figure out which area
10842 // we need to refresh
10843
10844#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
10845 wxRichTextParagraphLayoutBox* container = GetContainer();
10846 wxASSERT(container != NULL);
10847 if (!container)
10848 return;
10849
7051fa41
JS
10850 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10851 // If we had several actions, which only invalidate and leave layout until the
10852 // paint handler is called, then this might not be true. So we may need to switch
10853 // optimisation on only when we're simply adding text and not simultaneously
10854 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10855 // first, but of course this means we'll be doing it twice.
603f702b 10856 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41 10857 {
4ba36292
JS
10858 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10859 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
7051fa41
JS
10860 int lastY = firstVisiblePt.y + clientSize.y;
10861
603f702b
JS
10862 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10863 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
10864 while (node)
10865 {
10866 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10867 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10868 while (node2)
10869 {
10870 wxRichTextLine* line = node2->GetData();
10871 wxPoint pt = line->GetAbsolutePosition();
10872 wxRichTextRange range = line->GetAbsoluteRange();
10873
10874 if (pt.y > lastY)
10875 {
10876 node2 = wxRichTextLineList::compatibility_iterator();
10877 node = wxRichTextObjectList::compatibility_iterator();
10878 }
10879 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10880 {
10881 optimizationLineCharPositions.Add(range.GetStart());
10882 optimizationLineYPositions.Add(pt.y);
10883 }
10884
10885 if (node2)
10886 node2 = node2->GetNext();
10887 }
10888
10889 if (node)
10890 node = node->GetNext();
10891 }
10892 }
10893#endif
10894}
10895
5d7836c4
JS
10896bool wxRichTextAction::Do()
10897{
10898 m_buffer->Modify(true);
10899
603f702b
JS
10900 wxRichTextParagraphLayoutBox* container = GetContainer();
10901 wxASSERT(container != NULL);
10902 if (!container)
10903 return false;
10904
5d7836c4
JS
10905 switch (m_cmdId)
10906 {
10907 case wxRICHTEXT_INSERT:
10908 {
ea160b2e
JS
10909 // Store a list of line start character and y positions so we can figure out which area
10910 // we need to refresh
10911 wxArrayInt optimizationLineCharPositions;
10912 wxArrayInt optimizationLineYPositions;
10913
10914#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 10915 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
10916#endif
10917
603f702b
JS
10918 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10919 container->UpdateRanges();
10920
10921 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10922 // Layout() would stop prematurely at the top level.
10923 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 10924
603f702b 10925 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
10926
10927 // Character position to caret position
10928 newCaretPosition --;
10929
10930 // Don't take into account the last newline
5d7836c4
JS
10931 if (m_newParagraphs.GetPartialParagraph())
10932 newCaretPosition --;
46ee0e5b 10933 else
7c081bd2 10934 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
10935 {
10936 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10937 if (p->GetRange().GetLength() == 1)
10938 newCaretPosition --;
10939 }
5d7836c4 10940
603f702b 10941 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 10942
7051fa41 10943 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 10944
5912d19e 10945 wxRichTextEvent cmdEvent(
ce7fe42e 10946 wxEVT_RICHTEXT_CONTENT_INSERTED,
5912d19e
JS
10947 m_ctrl ? m_ctrl->GetId() : -1);
10948 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10949 cmdEvent.SetRange(GetRange());
10950 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10951 cmdEvent.SetContainer(container);
3e541562 10952
5912d19e 10953 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
10954
10955 break;
10956 }
10957 case wxRICHTEXT_DELETE:
10958 {
7051fa41
JS
10959 wxArrayInt optimizationLineCharPositions;
10960 wxArrayInt optimizationLineYPositions;
10961
10962#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10963 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10964#endif
10965
603f702b
JS
10966 container->DeleteRange(GetRange());
10967 container->UpdateRanges();
10968 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10969 // Layout() would stop prematurely at the top level.
10970 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 10971
6ccbca24 10972 long caretPos = GetRange().GetStart()-1;
603f702b 10973 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
10974 caretPos --;
10975
7051fa41 10976 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 10977
5912d19e 10978 wxRichTextEvent cmdEvent(
ce7fe42e 10979 wxEVT_RICHTEXT_CONTENT_DELETED,
5912d19e
JS
10980 m_ctrl ? m_ctrl->GetId() : -1);
10981 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10982 cmdEvent.SetRange(GetRange());
10983 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10984 cmdEvent.SetContainer(container);
3e541562 10985
5912d19e
JS
10986 m_buffer->SendEvent(cmdEvent);
10987
5d7836c4
JS
10988 break;
10989 }
10990 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10991 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10992 {
10993 ApplyParagraphs(GetNewParagraphs());
603f702b 10994
c4168888 10995 // Invalidate the whole buffer if there were floating objects
e12b91a3 10996 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10997 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10998 else
10999 {
11000 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11001 // Layout() would stop prematurely at the top level.
11002 container->InvalidateHierarchy(GetRange());
11003 }
603f702b
JS
11004
11005 UpdateAppearance(GetPosition());
11006
11007 wxRichTextEvent cmdEvent(
ce7fe42e 11008 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
11009 m_ctrl ? m_ctrl->GetId() : -1);
11010 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11011 cmdEvent.SetRange(GetRange());
11012 cmdEvent.SetPosition(GetRange().GetStart());
11013 cmdEvent.SetContainer(container);
11014
11015 m_buffer->SendEvent(cmdEvent);
11016
11017 break;
11018 }
11019 case wxRICHTEXT_CHANGE_ATTRIBUTES:
11020 {
11021 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
11022 if (obj)
11023 {
11024 wxRichTextAttr oldAttr = obj->GetAttributes();
11025 obj->GetAttributes() = m_attributes;
11026 m_attributes = oldAttr;
11027 }
11028
11029 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11030 // Layout() would stop prematurely at the top level.
c4168888 11031 // Invalidate the whole buffer if there were floating objects
e12b91a3 11032 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
11033 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
11034 else
11035 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
11036
11037 UpdateAppearance(GetPosition());
11038
5912d19e 11039 wxRichTextEvent cmdEvent(
ce7fe42e 11040 wxEVT_RICHTEXT_STYLE_CHANGED,
5912d19e
JS
11041 m_ctrl ? m_ctrl->GetId() : -1);
11042 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11043 cmdEvent.SetRange(GetRange());
11044 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 11045 cmdEvent.SetContainer(container);
3e541562 11046
5912d19e
JS
11047 m_buffer->SendEvent(cmdEvent);
11048
603f702b
JS
11049 break;
11050 }
11051 case wxRICHTEXT_CHANGE_OBJECT:
11052 {
11053 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
31be8400 11054 if (obj && m_object && m_ctrl)
603f702b 11055 {
31be8400
JS
11056 // The plan is to swap the current object with the stored, previous-state, clone
11057 // We can't get 'node' from the containing buffer (as it doesn't directly store objects)
11058 // so use the parent paragraph
11059 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
11060 wxCHECK_MSG(para, false, "Invalid parent paragraph");
bf5e92db
VZ
11061
11062 // The stored object, m_object, may have a stale parent paragraph. This would cause
11063 // a crash during layout, so use obj's parent para, which should be the correct one.
11064 // (An alternative would be to return the parent too from m_objectAddress.GetObject(),
11065 // or to set obj's parent there before returning)
11066 m_object->SetParent(para);
11067
31be8400 11068 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().Find(obj);
603f702b
JS
11069 if (node)
11070 {
11071 wxRichTextObject* obj = node->GetData();
11072 node->SetData(m_object);
11073 m_object = obj;
11074 }
11075 }
11076
11077 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11078 // Layout() would stop prematurely at the top level.
c4168888 11079 // Invalidate the whole buffer if there were floating objects
e12b91a3 11080 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
11081 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
11082 else
11083 container->InvalidateHierarchy(GetRange());
603f702b
JS
11084
11085 UpdateAppearance(GetPosition());
11086
11087 // TODO: send new kind of modification event
11088
5d7836c4
JS
11089 break;
11090 }
11091 default:
11092 break;
11093 }
11094
11095 return true;
11096}
11097
11098bool wxRichTextAction::Undo()
11099{
11100 m_buffer->Modify(true);
11101
603f702b
JS
11102 wxRichTextParagraphLayoutBox* container = GetContainer();
11103 wxASSERT(container != NULL);
11104 if (!container)
11105 return false;
11106
5d7836c4
JS
11107 switch (m_cmdId)
11108 {
11109 case wxRICHTEXT_INSERT:
11110 {
7051fa41
JS
11111 wxArrayInt optimizationLineCharPositions;
11112 wxArrayInt optimizationLineYPositions;
11113
11114#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11115 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
11116#endif
11117
603f702b
JS
11118 container->DeleteRange(GetRange());
11119 container->UpdateRanges();
7c9fdebe 11120
603f702b
JS
11121 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11122 // Layout() would stop prematurely at the top level.
11123 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
11124
11125 long newCaretPosition = GetPosition() - 1;
3e541562 11126
7051fa41 11127 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 11128
5912d19e 11129 wxRichTextEvent cmdEvent(
ce7fe42e 11130 wxEVT_RICHTEXT_CONTENT_DELETED,
5912d19e
JS
11131 m_ctrl ? m_ctrl->GetId() : -1);
11132 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11133 cmdEvent.SetRange(GetRange());
11134 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 11135 cmdEvent.SetContainer(container);
3e541562 11136
5912d19e
JS
11137 m_buffer->SendEvent(cmdEvent);
11138
5d7836c4
JS
11139 break;
11140 }
11141 case wxRICHTEXT_DELETE:
11142 {
7051fa41
JS
11143 wxArrayInt optimizationLineCharPositions;
11144 wxArrayInt optimizationLineYPositions;
11145
11146#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11147 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
11148#endif
11149
603f702b
JS
11150 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
11151 container->UpdateRanges();
7c9fdebe 11152
603f702b
JS
11153 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11154 // Layout() would stop prematurely at the top level.
11155 container->InvalidateHierarchy(GetRange());
5d7836c4 11156
7051fa41 11157 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 11158
5912d19e 11159 wxRichTextEvent cmdEvent(
ce7fe42e 11160 wxEVT_RICHTEXT_CONTENT_INSERTED,
5912d19e
JS
11161 m_ctrl ? m_ctrl->GetId() : -1);
11162 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11163 cmdEvent.SetRange(GetRange());
11164 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 11165 cmdEvent.SetContainer(container);
3e541562 11166
5912d19e
JS
11167 m_buffer->SendEvent(cmdEvent);
11168
5d7836c4
JS
11169 break;
11170 }
11171 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 11172 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
11173 {
11174 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
11175 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11176 // Layout() would stop prematurely at the top level.
11177 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
11178
11179 UpdateAppearance(GetPosition());
11180
5912d19e 11181 wxRichTextEvent cmdEvent(
ce7fe42e 11182 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
11183 m_ctrl ? m_ctrl->GetId() : -1);
11184 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11185 cmdEvent.SetRange(GetRange());
11186 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 11187 cmdEvent.SetContainer(container);
3e541562 11188
5912d19e
JS
11189 m_buffer->SendEvent(cmdEvent);
11190
5d7836c4
JS
11191 break;
11192 }
603f702b
JS
11193 case wxRICHTEXT_CHANGE_ATTRIBUTES:
11194 case wxRICHTEXT_CHANGE_OBJECT:
11195 {
11196 return Do();
11197 }
5d7836c4
JS
11198 default:
11199 break;
11200 }
11201
11202 return true;
11203}
11204
11205/// Update the control appearance
603f702b 11206void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 11207{
603f702b
JS
11208 wxRichTextParagraphLayoutBox* container = GetContainer();
11209 wxASSERT(container != NULL);
11210 if (!container)
11211 return;
11212
5d7836c4
JS
11213 if (m_ctrl)
11214 {
603f702b 11215 m_ctrl->SetFocusObject(container);
5d7836c4 11216 m_ctrl->SetCaretPosition(caretPosition);
603f702b 11217
5d7836c4
JS
11218 if (!m_ctrl->IsFrozen())
11219 {
603f702b
JS
11220 wxRect containerRect = container->GetRect();
11221
2f36e8dc 11222 m_ctrl->LayoutContent();
5d7836c4 11223
603f702b
JS
11224 // Refresh everything if there were floating objects or the container changed size
11225 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
e12b91a3 11226 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
603f702b
JS
11227 {
11228 m_ctrl->Refresh(false);
11229 }
11230 else
11231
11232#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11233 // Find refresh rectangle if we are in a position to optimise refresh
11234 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
11235 {
11236 size_t i;
11237
4ba36292
JS
11238 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
11239 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
603f702b
JS
11240
11241 // Start/end positions
11242 int firstY = 0;
11243 int lastY = firstVisiblePt.y + clientSize.y;
11244
11245 bool foundEnd = false;
11246
11247 // position offset - how many characters were inserted
11248 int positionOffset = GetRange().GetLength();
11249
11250 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
11251 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
11252 positionOffset = - positionOffset;
11253
11254 // find the first line which is being drawn at the same position as it was
11255 // before. Since we're talking about a simple insertion, we can assume
11256 // that the rest of the window does not need to be redrawn.
93a16a7d 11257 long pos = GetRange().GetStart();
603f702b 11258
93a16a7d 11259 wxRichTextParagraph* para = container->GetParagraphAtPosition(pos, false /* is not caret pos */);
603f702b
JS
11260 // Since we support floating layout, we should redraw the whole para instead of just
11261 // the first line touching the invalid range.
11262 if (para)
11263 {
93a16a7d
JS
11264 // In case something was drawn above the paragraph,
11265 // such as a line break, allow a little extra.
11266 firstY = para->GetPosition().y - 4;
603f702b
JS
11267 }
11268
11269 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
11270 while (node)
11271 {
11272 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
11273 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
11274 while (node2)
11275 {
11276 wxRichTextLine* line = node2->GetData();
11277 wxPoint pt = line->GetAbsolutePosition();
11278 wxRichTextRange range = line->GetAbsoluteRange();
11279
11280 // we want to find the first line that is in the same position
11281 // as before. This will mean we're at the end of the changed text.
11282
11283 if (pt.y > lastY) // going past the end of the window, no more info
11284 {
11285 node2 = wxRichTextLineList::compatibility_iterator();
11286 node = wxRichTextObjectList::compatibility_iterator();
11287 }
11288 // Detect last line in the buffer
11289 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
11290 {
11291 // If deleting text, make sure we refresh below as well as above
11292 if (positionOffset >= 0)
11293 {
11294 foundEnd = true;
11295 lastY = pt.y + line->GetSize().y;
11296 }
11297
11298 node2 = wxRichTextLineList::compatibility_iterator();
11299 node = wxRichTextObjectList::compatibility_iterator();
11300
11301 break;
11302 }
11303 else
11304 {
11305 // search for this line being at the same position as before
11306 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
11307 {
11308 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
11309 ((*optimizationLineYPositions)[i] == pt.y))
11310 {
11311 // Stop, we're now the same as we were
11312 foundEnd = true;
11313
93a16a7d 11314 lastY = pt.y + line->GetSize().y;
603f702b
JS
11315
11316 node2 = wxRichTextLineList::compatibility_iterator();
11317 node = wxRichTextObjectList::compatibility_iterator();
11318
11319 break;
11320 }
11321 }
11322 }
11323
11324 if (node2)
11325 node2 = node2->GetNext();
11326 }
11327
11328 if (node)
11329 node = node->GetNext();
11330 }
11331
11332 firstY = wxMax(firstVisiblePt.y, firstY);
11333 if (!foundEnd)
11334 lastY = firstVisiblePt.y + clientSize.y;
11335
11336 // Convert to device coordinates
4ba36292 11337 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
603f702b
JS
11338 m_ctrl->RefreshRect(rect);
11339 }
11340 else
1c13f06e 11341#endif
603f702b
JS
11342 m_ctrl->Refresh(false);
11343
11344 m_ctrl->PositionCaret();
4fe83b93
JS
11345
11346 // This causes styles to persist when doing programmatic
11347 // content creation except when Freeze/Thaw is used, so
11348 // disable this and check for the consequences.
11349 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 11350
5d7836c4 11351 if (sendUpdateEvent)
0ec1179b 11352 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 11353 }
7fe8059f 11354 }
5d7836c4
JS
11355}
11356
11357/// Replace the buffer paragraphs with the new ones.
0ca07313 11358void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 11359{
603f702b
JS
11360 wxRichTextParagraphLayoutBox* container = GetContainer();
11361 wxASSERT(container != NULL);
11362 if (!container)
11363 return;
11364
5d7836c4
JS
11365 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
11366 while (node)
11367 {
11368 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
11369 wxASSERT (para != NULL);
11370
11371 // We'll replace the existing paragraph by finding the paragraph at this position,
11372 // delete its node data, and setting a copy as the new node data.
11373 // TODO: make more efficient by simply swapping old and new paragraph objects.
11374
603f702b 11375 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
11376 if (existingPara)
11377 {
603f702b 11378 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
11379 if (bufferParaNode)
11380 {
11381 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 11382 newPara->SetParent(container);
5d7836c4
JS
11383
11384 bufferParaNode->SetData(newPara);
11385
11386 delete existingPara;
11387 }
11388 }
11389
11390 node = node->GetNext();
11391 }
11392}
11393
11394
11395/*!
11396 * wxRichTextRange
11397 * This stores beginning and end positions for a range of data.
11398 */
11399
603f702b
JS
11400WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11401
5d7836c4
JS
11402/// Limit this range to be within 'range'
11403bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11404{
11405 if (m_start < range.m_start)
11406 m_start = range.m_start;
11407
11408 if (m_end > range.m_end)
11409 m_end = range.m_end;
11410
11411 return true;
11412}
11413
11414/*!
11415 * wxRichTextImage implementation
11416 * This object represents an image.
11417 */
11418
bec80f4f 11419IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 11420
24777478 11421wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11422 wxRichTextObject(parent)
5d7836c4 11423{
23698b12 11424 Init();
cdaed652 11425 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
11426 if (charStyle)
11427 SetAttributes(*charStyle);
5d7836c4
JS
11428}
11429
24777478 11430wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11431 wxRichTextObject(parent)
5d7836c4 11432{
23698b12 11433 Init();
5d7836c4 11434 m_imageBlock = imageBlock;
4f32b3cf
JS
11435 if (charStyle)
11436 SetAttributes(*charStyle);
5d7836c4
JS
11437}
11438
914a4e23
JS
11439wxRichTextImage::~wxRichTextImage()
11440{
11441}
11442
23698b12
JS
11443void wxRichTextImage::Init()
11444{
11445 m_originalImageSize = wxSize(-1, -1);
11446}
11447
cdaed652 11448/// Create a cached image at the required size
914a4e23 11449bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
5d7836c4 11450{
23698b12
JS
11451 if (!m_imageBlock.IsOk())
11452 return false;
11453
11454 // If we have an original image size, use that to compute the cached bitmap size
11455 // instead of loading the image each time. This way we can avoid loading
11456 // the image so long as the new cached bitmap size hasn't changed.
11457
11458 wxImage image;
2798df59 11459 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
cdaed652 11460 {
23698b12 11461 m_imageCache = wxNullBitmap;
ce00f59b 11462
cdaed652
VZ
11463 m_imageBlock.Load(image);
11464 if (!image.IsOk())
11465 return false;
ce00f59b 11466
23698b12
JS
11467 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11468 }
11469
11470 int width = m_originalImageSize.GetWidth();
11471 int height = m_originalImageSize.GetHeight();
11472
11473 int parentWidth = 0;
11474 int parentHeight = 0;
bec80f4f 11475
23698b12
JS
11476 int maxWidth = -1;
11477 int maxHeight = -1;
11478
914a4e23
JS
11479 wxSize sz = parentSize;
11480 if (sz == wxDefaultSize)
23698b12 11481 {
914a4e23
JS
11482 if (GetParent() && GetParent()->GetParent())
11483 sz = GetParent()->GetParent()->GetCachedSize();
11484 }
ab6b1860 11485
914a4e23
JS
11486 if (sz != wxDefaultSize)
11487 {
11488 wxRichTextBuffer* buffer = GetBuffer();
11489 if (buffer)
11490 {
11491 // Find the actual space available when margin is taken into account
23698b12
JS
11492 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11493 marginRect = wxRect(0, 0, sz.x, sz.y);
914a4e23
JS
11494 if (GetParent() && GetParent()->GetParent())
11495 {
11496 buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11497 sz = contentRect.GetSize();
11498 }
23698b12 11499
914a4e23
JS
11500 // Use a minimum size to stop images becoming very small
11501 parentWidth = wxMax(100, sz.GetWidth());
11502 parentHeight = wxMax(100, sz.GetHeight());
23698b12 11503
914a4e23
JS
11504 if (buffer->GetRichTextCtrl())
11505 // Start with a maximum width of the control size, even if not specified by the content,
11506 // to minimize the amount of picture overlapping the right-hand side
11507 maxWidth = parentWidth;
cdaed652 11508 }
23698b12
JS
11509 }
11510
11511 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11512 {
11513 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11514 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11515 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11516 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11517 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11518 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11519 }
11520
11521 // Limit to max width
11522
11523 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11524 {
11525 int mw = -1;
11526
11527 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11528 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11529 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11530 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11531 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11532 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11533
11534 // If we already have a smaller max width due to the constraints of the control size,
11535 // don't use the larger max width.
11536 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11537 maxWidth = mw;
11538 }
11539
11540 if (maxWidth > 0 && width > maxWidth)
11541 width = maxWidth;
11542
11543 // Preserve the aspect ratio
11544 if (width != m_originalImageSize.GetWidth())
11545 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11546
11547 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11548 {
11549 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11550 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11551 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11552 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11553 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11554 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11555
11556 // Preserve the aspect ratio
11557 if (height != m_originalImageSize.GetHeight())
11558 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11559 }
11560
11561 // Limit to max height
11562
11563 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11564 {
11565 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11566 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11567 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11568 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11569 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11570 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11571 }
11572
11573 if (maxHeight > 0 && height > maxHeight)
11574 {
11575 height = maxHeight;
11576
11577 // Preserve the aspect ratio
11578 if (height != m_originalImageSize.GetHeight())
11579 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11580 }
11581
ab6b1860
JS
11582 // Prevent the use of zero size
11583 width = wxMax(1, width);
11584 height = wxMax(1, height);
11585
23698b12
JS
11586 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11587 {
11588 // Do nothing, we didn't need to change the image cache
11589 }
11590 else
11591 {
11592 if (!image.IsOk())
cdaed652 11593 {
23698b12
JS
11594 m_imageBlock.Load(image);
11595 if (!image.IsOk())
11596 return false;
cdaed652 11597 }
5d7836c4 11598
cdaed652
VZ
11599 if (image.GetWidth() == width && image.GetHeight() == height)
11600 m_imageCache = wxBitmap(image);
11601 else
11602 {
11603 // If the original width and height is small, e.g. 400 or below,
11604 // scale up and then down to improve image quality. This can make
11605 // a big difference, with not much performance hit.
11606 int upscaleThreshold = 400;
11607 wxImage img;
11608 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11609 {
11610 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11611 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11612 }
11613 else
11614 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11615 m_imageCache = wxBitmap(img);
11616 }
11617 }
ce00f59b 11618
cdaed652 11619 return m_imageCache.IsOk();
5d7836c4
JS
11620}
11621
5d7836c4 11622/// Draw the item
20d09da5 11623bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 11624{
603f702b
JS
11625 if (!IsShown())
11626 return true;
11627
cdaed652
VZ
11628 // Don't need cached size AFAIK
11629 // wxSize size = GetCachedSize();
11630 if (!LoadImageCache(dc))
5d7836c4 11631 return false;
ce00f59b 11632
8db2e3ef
JS
11633 wxRichTextAttr attr(GetAttributes());
11634 context.ApplyVirtualAttributes(attr, this);
11635
11636 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b 11637
603f702b
JS
11638 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11639 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11640 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 11641 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11642
11643 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 11644
a70eb13e 11645 if (selection.WithinSelection(GetRange().GetStart(), this))
5d7836c4 11646 {
ecb5fbf1
JS
11647 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11648 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 11649 dc.SetLogicalFunction(wxINVERT);
603f702b 11650 dc.DrawRectangle(contentRect);
5d7836c4
JS
11651 dc.SetLogicalFunction(wxCOPY);
11652 }
11653
11654 return true;
11655}
11656
11657/// Lay the item out
8db2e3ef 11658bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 11659{
cdaed652
VZ
11660 if (!LoadImageCache(dc))
11661 return false;
5d7836c4 11662
603f702b
JS
11663 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11664 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11665 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
11666
11667 wxRichTextAttr attr(GetAttributes());
11668 context.ApplyVirtualAttributes(attr, this);
11669
11670 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11671
11672 wxSize overallSize = marginRect.GetSize();
11673
11674 SetCachedSize(overallSize);
11675 SetMaxSize(overallSize);
11676 SetMinSize(overallSize);
cdaed652 11677 SetPosition(rect.GetPosition());
5d7836c4
JS
11678
11679 return true;
11680}
11681
11682/// Get/set the object size for the given range. Returns false if the range
11683/// is invalid for this object.
914a4e23 11684bool 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
11685{
11686 if (!range.IsWithin(GetRange()))
11687 return false;
11688
914a4e23 11689 if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
31778480 11690 {
cdaed652
VZ
11691 size.x = 0; size.y = 0;
11692 if (partialExtents)
31778480 11693 partialExtents->Add(0);
cdaed652 11694 return false;
31778480 11695 }
ce00f59b 11696
8db2e3ef
JS
11697 wxRichTextAttr attr(GetAttributes());
11698 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11699
603f702b
JS
11700 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11701 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11702 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef 11703 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11704
11705 wxSize overallSize = marginRect.GetSize();
31778480 11706
cdaed652 11707 if (partialExtents)
603f702b 11708 partialExtents->Add(overallSize.x);
5d7836c4 11709
603f702b 11710 size = overallSize;
5d7836c4
JS
11711
11712 return true;
11713}
11714
603f702b
JS
11715// Get the 'natural' size for an object. For an image, it would be the
11716// image size.
11717wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11718{
11719 wxTextAttrSize size;
11720 if (GetImageCache().IsOk())
11721 {
11722 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11723 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11724 }
11725 return size;
11726}
11727
11728
5d7836c4
JS
11729/// Copy
11730void wxRichTextImage::Copy(const wxRichTextImage& obj)
11731{
bec80f4f 11732 wxRichTextObject::Copy(obj);
59509217 11733
5d7836c4 11734 m_imageBlock = obj.m_imageBlock;
23698b12 11735 m_originalImageSize = obj.m_originalImageSize;
5d7836c4
JS
11736}
11737
cdaed652
VZ
11738/// Edit properties via a GUI
11739bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11740{
603f702b
JS
11741 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11742 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
11743
11744 if (imageDlg.ShowModal() == wxID_OK)
11745 {
603f702b
JS
11746 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11747 // indeterminate in the object.
11748 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
11749 return true;
11750 }
11751 else
11752 return false;
11753}
11754
5d7836c4
JS
11755/*!
11756 * Utilities
11757 *
11758 */
11759
11760/// Compare two attribute objects
24777478 11761bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 11762{
38f833b1 11763 return (attr1 == attr2);
5d7836c4
JS
11764}
11765
44cc96a8
JS
11766/// Compare tabs
11767bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11768{
11769 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
11770 return false;
11771
44cc96a8
JS
11772 size_t i;
11773 for (i = 0; i < tabs1.GetCount(); i++)
11774 {
11775 if (tabs1[i] != tabs2[i])
11776 return false;
11777 }
11778 return true;
11779}
5d7836c4 11780
24777478 11781bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
11782{
11783 return destStyle.Apply(style, compareWith);
11784}
5d7836c4 11785
44cc96a8 11786// Remove attributes
24777478 11787bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 11788{
24777478 11789 return destStyle.RemoveStyle(style);
44cc96a8 11790}
5d7836c4 11791
44cc96a8
JS
11792/// Combine two bitlists, specifying the bits of interest with separate flags.
11793bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11794{
24777478 11795 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 11796}
5d7836c4 11797
44cc96a8
JS
11798/// Compare two bitlists
11799bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11800{
24777478 11801 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 11802}
38f833b1 11803
44cc96a8 11804/// Split into paragraph and character styles
24777478 11805bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 11806{
24777478 11807 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 11808}
5d7836c4 11809
44cc96a8
JS
11810/// Convert a decimal to Roman numerals
11811wxString wxRichTextDecimalToRoman(long n)
11812{
11813 static wxArrayInt decimalNumbers;
11814 static wxArrayString romanNumbers;
5d7836c4 11815
44cc96a8
JS
11816 // Clean up arrays
11817 if (n == -1)
11818 {
11819 decimalNumbers.Clear();
11820 romanNumbers.Clear();
11821 return wxEmptyString;
11822 }
5d7836c4 11823
44cc96a8
JS
11824 if (decimalNumbers.GetCount() == 0)
11825 {
11826 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 11827
44cc96a8
JS
11828 wxRichTextAddDecRom(1000, wxT("M"));
11829 wxRichTextAddDecRom(900, wxT("CM"));
11830 wxRichTextAddDecRom(500, wxT("D"));
11831 wxRichTextAddDecRom(400, wxT("CD"));
11832 wxRichTextAddDecRom(100, wxT("C"));
11833 wxRichTextAddDecRom(90, wxT("XC"));
11834 wxRichTextAddDecRom(50, wxT("L"));
11835 wxRichTextAddDecRom(40, wxT("XL"));
11836 wxRichTextAddDecRom(10, wxT("X"));
11837 wxRichTextAddDecRom(9, wxT("IX"));
11838 wxRichTextAddDecRom(5, wxT("V"));
11839 wxRichTextAddDecRom(4, wxT("IV"));
11840 wxRichTextAddDecRom(1, wxT("I"));
11841 }
5d7836c4 11842
44cc96a8
JS
11843 int i = 0;
11844 wxString roman;
ea160b2e 11845
44cc96a8 11846 while (n > 0 && i < 13)
42688aea 11847 {
44cc96a8
JS
11848 if (n >= decimalNumbers[i])
11849 {
11850 n -= decimalNumbers[i];
11851 roman += romanNumbers[i];
11852 }
11853 else
11854 {
11855 i ++;
11856 }
42688aea 11857 }
44cc96a8
JS
11858 if (roman.IsEmpty())
11859 roman = wxT("0");
11860 return roman;
11861}
42688aea 11862
44cc96a8
JS
11863/*!
11864 * wxRichTextFileHandler
11865 * Base class for file handlers
11866 */
4d6d8bf4 11867
44cc96a8 11868IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 11869
44cc96a8
JS
11870#if wxUSE_FFILE && wxUSE_STREAMS
11871bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 11872{
44cc96a8 11873 wxFFileInputStream stream(filename);
a1b806b9 11874 if (stream.IsOk())
44cc96a8 11875 return LoadFile(buffer, stream);
5d7836c4 11876
44cc96a8
JS
11877 return false;
11878}
5d7836c4 11879
44cc96a8
JS
11880bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11881{
11882 wxFFileOutputStream stream(filename);
a1b806b9 11883 if (stream.IsOk())
44cc96a8 11884 return SaveFile(buffer, stream);
5d7836c4 11885
44cc96a8
JS
11886 return false;
11887}
11888#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 11889
44cc96a8
JS
11890/// Can we handle this filename (if using files)? By default, checks the extension.
11891bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11892{
11893 wxString path, file, ext;
a51e601e 11894 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 11895
44cc96a8
JS
11896 return (ext.Lower() == GetExtension());
11897}
5d7836c4 11898
44cc96a8
JS
11899/*!
11900 * wxRichTextTextHandler
11901 * Plain text handler
11902 */
5d7836c4 11903
44cc96a8 11904IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 11905
44cc96a8
JS
11906#if wxUSE_STREAMS
11907bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11908{
11909 if (!stream.IsOk())
797e38dd
JS
11910 return false;
11911
44cc96a8
JS
11912 wxString str;
11913 int lastCh = 0;
5d7836c4 11914
44cc96a8
JS
11915 while (!stream.Eof())
11916 {
11917 int ch = stream.GetC();
5d7836c4 11918
44cc96a8
JS
11919 if (!stream.Eof())
11920 {
11921 if (ch == 10 && lastCh != 13)
11922 str += wxT('\n');
5d7836c4 11923
44cc96a8
JS
11924 if (ch > 0 && ch != 10)
11925 str += wxChar(ch);
5d7836c4 11926
44cc96a8
JS
11927 lastCh = ch;
11928 }
11929 }
5d7836c4 11930
44cc96a8
JS
11931 buffer->ResetAndClearCommands();
11932 buffer->Clear();
11933 buffer->AddParagraphs(str);
11934 buffer->UpdateRanges();
5d7836c4 11935
44cc96a8
JS
11936 return true;
11937}
5d7836c4 11938
44cc96a8
JS
11939bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11940{
11941 if (!stream.IsOk())
5d7836c4
JS
11942 return false;
11943
44cc96a8 11944 wxString text = buffer->GetText();
38f833b1 11945
44cc96a8
JS
11946 wxString newLine = wxRichTextLineBreakChar;
11947 text.Replace(newLine, wxT("\n"));
5d7836c4 11948
44cc96a8 11949 wxCharBuffer buf = text.ToAscii();
5d7836c4 11950
44cc96a8
JS
11951 stream.Write((const char*) buf, text.length());
11952 return true;
11953}
11954#endif // wxUSE_STREAMS
5d7836c4 11955
44cc96a8
JS
11956/*
11957 * Stores information about an image, in binary in-memory form
11958 */
59509217 11959
44cc96a8
JS
11960wxRichTextImageBlock::wxRichTextImageBlock()
11961{
11962 Init();
11963}
5d7836c4 11964
44cc96a8
JS
11965wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11966{
11967 Init();
11968 Copy(block);
11969}
ea160b2e 11970
44cc96a8
JS
11971wxRichTextImageBlock::~wxRichTextImageBlock()
11972{
5276b0a5 11973 wxDELETEA(m_data);
5d7836c4
JS
11974}
11975
44cc96a8 11976void wxRichTextImageBlock::Init()
5d7836c4
JS
11977{
11978 m_data = NULL;
11979 m_dataSize = 0;
d75a69e8 11980 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11981}
11982
11983void wxRichTextImageBlock::Clear()
11984{
5276b0a5 11985 wxDELETEA(m_data);
5d7836c4 11986 m_dataSize = 0;
d75a69e8 11987 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11988}
11989
11990
11991// Load the original image into a memory block.
11992// If the image is not a JPEG, we must convert it into a JPEG
11993// to conserve space.
11994// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11995// load the image a 2nd time.
11996
d75a69e8
FM
11997bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11998 wxImage& image, bool convertToJPEG)
5d7836c4
JS
11999{
12000 m_imageType = imageType;
12001
12002 wxString filenameToRead(filename);
7fe8059f 12003 bool removeFile = false;
5d7836c4 12004
62891c87 12005 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 12006 return false; // Could not determine image type
5d7836c4
JS
12007
12008 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
12009 {
a51e601e
FM
12010 wxString tempFile =
12011 wxFileName::CreateTempFileName(_("image"));
5d7836c4 12012
a51e601e 12013 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
12014
12015 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
12016 filenameToRead = tempFile;
7fe8059f 12017 removeFile = true;
5d7836c4
JS
12018
12019 m_imageType = wxBITMAP_TYPE_JPEG;
12020 }
12021 wxFile file;
12022 if (!file.Open(filenameToRead))
7fe8059f 12023 return false;
5d7836c4
JS
12024
12025 m_dataSize = (size_t) file.Length();
12026 file.Close();
12027
12028 if (m_data)
12029 delete[] m_data;
12030 m_data = ReadBlock(filenameToRead, m_dataSize);
12031
12032 if (removeFile)
12033 wxRemoveFile(filenameToRead);
12034
12035 return (m_data != NULL);
12036}
12037
12038// Make an image block from the wxImage in the given
12039// format.
d75a69e8 12040bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 12041{
5d7836c4
JS
12042 image.SetOption(wxT("quality"), quality);
12043
62891c87 12044 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 12045 return false; // Could not determine image type
5d7836c4 12046
cdaed652
VZ
12047 return DoMakeImageBlock(image, imageType);
12048}
12049
12050// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
12051bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
12052{
12053 if (imageType == wxBITMAP_TYPE_INVALID)
12054 return false; // Could not determine image type
ce00f59b 12055
cdaed652
VZ
12056 return DoMakeImageBlock(image, imageType);
12057}
7fe8059f 12058
cdaed652
VZ
12059// Makes the image block
12060bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
12061{
12062 wxMemoryOutputStream memStream;
12063 if (!image.SaveFile(memStream, imageType))
5d7836c4 12064 {
7fe8059f 12065 return false;
5d7836c4 12066 }
ce00f59b 12067
cdaed652
VZ
12068 unsigned char* block = new unsigned char[memStream.GetSize()];
12069 if (!block)
377c1ba4 12070 return false;
ce00f59b 12071
5d7836c4
JS
12072 if (m_data)
12073 delete[] m_data;
cdaed652 12074 m_data = block;
ce00f59b
VZ
12075
12076 m_imageType = imageType;
cdaed652 12077 m_dataSize = memStream.GetSize();
5d7836c4 12078
cdaed652 12079 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
12080
12081 return (m_data != NULL);
12082}
12083
5d7836c4
JS
12084// Write to a file
12085bool wxRichTextImageBlock::Write(const wxString& filename)
12086{
12087 return WriteBlock(filename, m_data, m_dataSize);
12088}
12089
12090void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
12091{
12092 m_imageType = block.m_imageType;
5276b0a5 12093 wxDELETEA(m_data);
5d7836c4
JS
12094 m_dataSize = block.m_dataSize;
12095 if (m_dataSize == 0)
12096 return;
12097
12098 m_data = new unsigned char[m_dataSize];
12099 unsigned int i;
12100 for (i = 0; i < m_dataSize; i++)
12101 m_data[i] = block.m_data[i];
12102}
12103
12104//// Operators
12105void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
12106{
12107 Copy(block);
12108}
12109
12110// Load a wxImage from the block
12111bool wxRichTextImageBlock::Load(wxImage& image)
12112{
12113 if (!m_data)
7fe8059f 12114 return false;
5d7836c4
JS
12115
12116 // Read in the image.
0ca07313 12117#if wxUSE_STREAMS
5d7836c4
JS
12118 wxMemoryInputStream mstream(m_data, m_dataSize);
12119 bool success = image.LoadFile(mstream, GetImageType());
12120#else
a51e601e
FM
12121 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
12122 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
12123
12124 if (!WriteBlock(tempFile, m_data, m_dataSize))
12125 {
7fe8059f 12126 return false;
5d7836c4
JS
12127 }
12128 success = image.LoadFile(tempFile, GetImageType());
12129 wxRemoveFile(tempFile);
12130#endif
12131
12132 return success;
12133}
12134
12135// Write data in hex to a stream
12136bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
12137{
4dc7ae1a
JS
12138 if (m_dataSize == 0)
12139 return true;
12140
12141 int bufSize = 100000;
a3c12576
JS
12142 if (int(2*m_dataSize) < bufSize)
12143 bufSize = 2*m_dataSize;
4dc7ae1a 12144 char* buf = new char[bufSize+1];
351c0647
JS
12145
12146 int left = m_dataSize;
12147 int n, i, j;
12148 j = 0;
12149 while (left > 0)
5d7836c4 12150 {
351c0647
JS
12151 if (left*2 > bufSize)
12152 {
12153 n = bufSize; left -= (bufSize/2);
12154 }
12155 else
12156 {
12157 n = left*2; left = 0;
12158 }
7fe8059f 12159
351c0647
JS
12160 char* b = buf;
12161 for (i = 0; i < (n/2); i++)
12162 {
f728025e 12163 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
12164 b += 2; j ++;
12165 }
5d7836c4 12166
351c0647
JS
12167 buf[n] = 0;
12168 stream.Write((const char*) buf, n);
12169 }
4dc7ae1a 12170 delete[] buf;
5d7836c4
JS
12171 return true;
12172}
12173
12174// Read data in hex from a stream
d75a69e8 12175bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
12176{
12177 int dataSize = length/2;
12178
12179 if (m_data)
12180 delete[] m_data;
12181
046fce47
FM
12182 // create a null terminated temporary string:
12183 char str[3];
12184 str[2] = '\0';
12185
5d7836c4
JS
12186 m_data = new unsigned char[dataSize];
12187 int i;
12188 for (i = 0; i < dataSize; i ++)
12189 {
c9f78968
VS
12190 str[0] = (char)stream.GetC();
12191 str[1] = (char)stream.GetC();
5d7836c4 12192
a9465653 12193 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
12194 }
12195
12196 m_dataSize = dataSize;
12197 m_imageType = imageType;
12198
12199 return true;
12200}
12201
5d7836c4
JS
12202// Allocate and read from stream as a block of memory
12203unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
12204{
12205 unsigned char* block = new unsigned char[size];
12206 if (!block)
12207 return NULL;
12208
12209 stream.Read(block, size);
12210
12211 return block;
12212}
12213
12214unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
12215{
12216 wxFileInputStream stream(filename);
a1b806b9 12217 if (!stream.IsOk())
5d7836c4
JS
12218 return NULL;
12219
12220 return ReadBlock(stream, size);
12221}
12222
12223// Write memory block to stream
12224bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
12225{
12226 stream.Write((void*) block, size);
12227 return stream.IsOk();
12228
12229}
12230
12231// Write memory block to file
12232bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
12233{
12234 wxFileOutputStream outStream(filename);
a1b806b9 12235 if (!outStream.IsOk())
7fe8059f 12236 return false;
5d7836c4
JS
12237
12238 return WriteBlock(outStream, block, size);
12239}
12240
d2d0adc7
JS
12241// Gets the extension for the block's type
12242wxString wxRichTextImageBlock::GetExtension() const
12243{
12244 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
12245 if (handler)
12246 return handler->GetExtension();
12247 else
12248 return wxEmptyString;
12249}
12250
0ca07313
JS
12251#if wxUSE_DATAOBJ
12252
12253/*!
12254 * The data object for a wxRichTextBuffer
12255 */
12256
bafc4eac 12257const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxRichText");
0ca07313
JS
12258
12259wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
12260{
12261 m_richTextBuffer = richTextBuffer;
12262
12263 // this string should uniquely identify our format, but is otherwise
12264 // arbitrary
12265 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
12266
12267 SetFormat(m_formatRichTextBuffer);
12268}
12269
12270wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
12271{
12272 delete m_richTextBuffer;
12273}
12274
12275// after a call to this function, the richTextBuffer is owned by the caller and it
12276// is responsible for deleting it!
12277wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
12278{
12279 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
12280 m_richTextBuffer = NULL;
12281
12282 return richTextBuffer;
12283}
12284
12285wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
12286{
12287 return m_formatRichTextBuffer;
12288}
12289
12290size_t wxRichTextBufferDataObject::GetDataSize() const
12291{
12292 if (!m_richTextBuffer)
12293 return 0;
12294
12295 wxString bufXML;
12296
12297 {
12298 wxStringOutputStream stream(& bufXML);
12299 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
12300 {
12301 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12302 return 0;
12303 }
12304 }
12305
12306#if wxUSE_UNICODE
12307 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
12308 return strlen(buffer) + 1;
12309#else
12310 return bufXML.Length()+1;
12311#endif
12312}
12313
12314bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
12315{
12316 if (!pBuf || !m_richTextBuffer)
12317 return false;
12318
12319 wxString bufXML;
12320
12321 {
12322 wxStringOutputStream stream(& bufXML);
12323 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
12324 {
12325 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12326 return 0;
12327 }
12328 }
12329
12330#if wxUSE_UNICODE
12331 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
12332 size_t len = strlen(buffer);
12333 memcpy((char*) pBuf, (const char*) buffer, len);
12334 ((char*) pBuf)[len] = 0;
12335#else
12336 size_t len = bufXML.Length();
12337 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
12338 ((char*) pBuf)[len] = 0;
12339#endif
12340
12341 return true;
12342}
12343
12344bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
12345{
5276b0a5 12346 wxDELETE(m_richTextBuffer);
0ca07313
JS
12347
12348 wxString bufXML((const char*) buf, wxConvUTF8);
12349
12350 m_richTextBuffer = new wxRichTextBuffer;
12351
12352 wxStringInputStream stream(bufXML);
12353 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
12354 {
12355 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12356
5276b0a5 12357 wxDELETE(m_richTextBuffer);
0ca07313
JS
12358
12359 return false;
12360 }
12361 return true;
12362}
12363
12364#endif
12365 // wxUSE_DATAOBJ
12366
44cc96a8
JS
12367
12368/*
12369 * wxRichTextFontTable
12370 * Manages quick access to a pool of fonts for rendering rich text
12371 */
12372
d65381ac 12373WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
12374
12375class wxRichTextFontTableData: public wxObjectRefData
12376{
12377public:
12378 wxRichTextFontTableData() {}
12379
32423dd8 12380 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
44cc96a8
JS
12381
12382 wxRichTextFontTableHashMap m_hashMap;
12383};
12384
32423dd8 12385wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
44cc96a8
JS
12386{
12387 wxString facename(fontSpec.GetFontFaceName());
44cc96a8 12388
32423dd8
JS
12389 int fontSize = fontSpec.GetFontSize();
12390 if (fontScale != 1.0)
12391 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12392
12393 wxString units;
12394 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12395 units = wxT("px");
12396 else
12397 units = wxT("pt");
12398 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12399 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12400 facename.c_str(), (int) fontSpec.GetFontEncoding());
12401
12402 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
44cc96a8
JS
12403 if ( entry == m_hashMap.end() )
12404 {
32423dd8
JS
12405 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12406 {
b42e4b3e 12407 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
32423dd8
JS
12408 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12409 font.SetStrikethrough(true);
12410 m_hashMap[spec] = font;
12411 return font;
12412 }
12413 else
12414 {
5a0e33af 12415 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
32423dd8
JS
12416 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12417 font.SetStrikethrough(true);
12418
12419 m_hashMap[spec] = font;
12420 return font;
12421 }
44cc96a8
JS
12422 }
12423 else
12424 {
12425 return entry->second;
12426 }
12427}
12428
12429IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12430
12431wxRichTextFontTable::wxRichTextFontTable()
12432{
12433 m_refData = new wxRichTextFontTableData;
32423dd8 12434 m_fontScale = 1.0;
44cc96a8
JS
12435}
12436
12437wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 12438 : wxObject()
44cc96a8
JS
12439{
12440 (*this) = table;
12441}
12442
12443wxRichTextFontTable::~wxRichTextFontTable()
12444{
12445 UnRef();
12446}
12447
12448bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12449{
12450 return (m_refData == table.m_refData);
12451}
12452
12453void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12454{
12455 Ref(table);
32423dd8 12456 m_fontScale = table.m_fontScale;
44cc96a8
JS
12457}
12458
24777478 12459wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
12460{
12461 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12462 if (data)
32423dd8 12463 return data->FindFont(fontSpec, m_fontScale);
44cc96a8
JS
12464 else
12465 return wxFont();
12466}
12467
12468void wxRichTextFontTable::Clear()
12469{
12470 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12471 if (data)
12472 data->m_hashMap.clear();
12473}
12474
32423dd8
JS
12475void wxRichTextFontTable::SetFontScale(double fontScale)
12476{
12477 if (fontScale != m_fontScale)
12478 Clear();
12479 m_fontScale = fontScale;
12480}
12481
24777478
JS
12482// wxTextBoxAttr
12483
24777478
JS
12484void wxTextBoxAttr::Reset()
12485{
12486 m_flags = 0;
603f702b
JS
12487 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12488 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12489 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12490 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 12491 m_boxStyleName = wxEmptyString;
bec80f4f 12492
24777478
JS
12493 m_margins.Reset();
12494 m_padding.Reset();
12495 m_position.Reset();
12496
603f702b 12497 m_size.Reset();
303f0be7
JS
12498 m_minSize.Reset();
12499 m_maxSize.Reset();
24777478
JS
12500
12501 m_border.Reset();
12502 m_outline.Reset();
12503}
12504
12505// Equality test
12506bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12507{
12508 return (
12509 m_flags == attr.m_flags &&
12510 m_floatMode == attr.m_floatMode &&
12511 m_clearMode == attr.m_clearMode &&
12512 m_collapseMode == attr.m_collapseMode &&
603f702b 12513 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 12514
24777478
JS
12515 m_margins == attr.m_margins &&
12516 m_padding == attr.m_padding &&
12517 m_position == attr.m_position &&
12518
603f702b 12519 m_size == attr.m_size &&
303f0be7
JS
12520 m_minSize == attr.m_minSize &&
12521 m_maxSize == attr.m_maxSize &&
24777478
JS
12522
12523 m_border == attr.m_border &&
2f987d83
JS
12524 m_outline == attr.m_outline &&
12525
12526 m_boxStyleName == attr.m_boxStyleName
24777478
JS
12527 );
12528}
12529
12530// Partial equality test
32423dd8 12531bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
24777478 12532{
32423dd8
JS
12533 if (!weakTest &&
12534 ((!HasFloatMode() && attr.HasFloatMode()) ||
12535 (!HasClearMode() && attr.HasClearMode()) ||
12536 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12537 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12538 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12539 {
12540 return false;
12541 }
24777478
JS
12542 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12543 return false;
12544
12545 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12546 return false;
12547
12548 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12549 return false;
12550
603f702b
JS
12551 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12552 return false;
12553
2f987d83
JS
12554 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12555 return false;
12556
24777478
JS
12557 // Position
12558
32423dd8 12559 if (!m_position.EqPartial(attr.m_position, weakTest))
24777478
JS
12560 return false;
12561
303f0be7
JS
12562 // Size
12563
32423dd8 12564 if (!m_size.EqPartial(attr.m_size, weakTest))
303f0be7 12565 return false;
32423dd8 12566 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
303f0be7 12567 return false;
32423dd8 12568 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
303f0be7
JS
12569 return false;
12570
24777478
JS
12571 // Margins
12572
32423dd8 12573 if (!m_margins.EqPartial(attr.m_margins, weakTest))
24777478
JS
12574 return false;
12575
12576 // Padding
12577
32423dd8 12578 if (!m_padding.EqPartial(attr.m_padding, weakTest))
24777478
JS
12579 return false;
12580
12581 // Border
12582
32423dd8 12583 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
24777478
JS
12584 return false;
12585
12586 // Outline
12587
32423dd8 12588 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
24777478
JS
12589 return false;
12590
12591 return true;
12592}
12593
12594// Merges the given attributes. If compareWith
12595// is non-NULL, then it will be used to mask out those attributes that are the same in style
12596// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12597bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12598{
12599 if (attr.HasFloatMode())
12600 {
12601 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12602 SetFloatMode(attr.GetFloatMode());
12603 }
12604
12605 if (attr.HasClearMode())
12606 {
12607 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12608 SetClearMode(attr.GetClearMode());
12609 }
12610
12611 if (attr.HasCollapseBorders())
12612 {
12613 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
12614 SetCollapseBorders(attr.GetCollapseBorders());
12615 }
12616
12617 if (attr.HasVerticalAlignment())
12618 {
12619 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12620 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 12621 }
bec80f4f 12622
2f987d83
JS
12623 if (attr.HasBoxStyleName())
12624 {
12625 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12626 SetBoxStyleName(attr.GetBoxStyleName());
12627 }
12628
bec80f4f
JS
12629 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12630 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12631 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 12632
603f702b 12633 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
12634 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12635 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 12636
bec80f4f
JS
12637 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12638 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
12639
12640 return true;
12641}
12642
12643// Remove specified attributes from this object
12644bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12645{
12646 if (attr.HasFloatMode())
12647 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12648
12649 if (attr.HasClearMode())
12650 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12651
12652 if (attr.HasCollapseBorders())
12653 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12654
603f702b
JS
12655 if (attr.HasVerticalAlignment())
12656 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12657
2f987d83
JS
12658 if (attr.HasBoxStyleName())
12659 {
12660 SetBoxStyleName(wxEmptyString);
12661 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12662 }
12663
24777478
JS
12664 m_margins.RemoveStyle(attr.m_margins);
12665 m_padding.RemoveStyle(attr.m_padding);
12666 m_position.RemoveStyle(attr.m_position);
12667
603f702b 12668 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
12669 m_minSize.RemoveStyle(attr.m_minSize);
12670 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
12671
12672 m_border.RemoveStyle(attr.m_border);
12673 m_outline.RemoveStyle(attr.m_outline);
12674
12675 return true;
12676}
12677
12678// Collects the attributes that are common to a range of content, building up a note of
12679// which attributes are absent in some objects and which clash in some objects.
12680void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12681{
12682 if (attr.HasFloatMode())
12683 {
12684 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12685 {
12686 if (HasFloatMode())
12687 {
12688 if (GetFloatMode() != attr.GetFloatMode())
12689 {
12690 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12691 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12692 }
12693 }
12694 else
12695 SetFloatMode(attr.GetFloatMode());
12696 }
12697 }
12698 else
12699 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 12700
24777478
JS
12701 if (attr.HasClearMode())
12702 {
12703 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12704 {
12705 if (HasClearMode())
12706 {
12707 if (GetClearMode() != attr.GetClearMode())
12708 {
12709 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12710 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12711 }
12712 }
12713 else
12714 SetClearMode(attr.GetClearMode());
12715 }
12716 }
12717 else
12718 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12719
12720 if (attr.HasCollapseBorders())
12721 {
12722 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12723 {
12724 if (HasCollapseBorders())
12725 {
12726 if (GetCollapseBorders() != attr.GetCollapseBorders())
12727 {
12728 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12729 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12730 }
12731 }
12732 else
12733 SetCollapseBorders(attr.GetCollapseBorders());
12734 }
12735 }
12736 else
12737 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 12738
603f702b
JS
12739 if (attr.HasVerticalAlignment())
12740 {
12741 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12742 {
12743 if (HasVerticalAlignment())
12744 {
12745 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12746 {
12747 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12748 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12749 }
12750 }
12751 else
12752 SetVerticalAlignment(attr.GetVerticalAlignment());
12753 }
12754 }
12755 else
12756 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12757
2f987d83
JS
12758 if (attr.HasBoxStyleName())
12759 {
12760 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12761 {
12762 if (HasBoxStyleName())
12763 {
12764 if (GetBoxStyleName() != attr.GetBoxStyleName())
12765 {
12766 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12767 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12768 }
12769 }
12770 else
12771 SetBoxStyleName(attr.GetBoxStyleName());
12772 }
12773 }
12774 else
12775 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12776
24777478
JS
12777 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12778 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12779 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12780
603f702b 12781 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
12782 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12783 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
12784
12785 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12786 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12787}
12788
eb3d8a33
JS
12789bool wxTextBoxAttr::IsDefault() const
12790{
12791 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 12792 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
12793 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12794}
12795
24777478
JS
12796// wxRichTextAttr
12797
12798void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12799{
bec80f4f
JS
12800 wxTextAttr::Copy(attr);
12801
24777478
JS
12802 m_textBoxAttr = attr.m_textBoxAttr;
12803}
12804
12805bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12806{
12807 if (!(wxTextAttr::operator==(attr)))
12808 return false;
bec80f4f 12809
24777478
JS
12810 return (m_textBoxAttr == attr.m_textBoxAttr);
12811}
12812
32423dd8
JS
12813// Partial equality test
12814bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
24777478 12815{
32423dd8 12816 if (!(wxTextAttr::EqPartial(attr, weakTest)))
24777478 12817 return false;
bec80f4f 12818
32423dd8 12819 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
24777478
JS
12820}
12821
12822// Merges the given attributes. If compareWith
12823// is non-NULL, then it will be used to mask out those attributes that are the same in style
12824// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12825bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12826{
12827 wxTextAttr::Apply(style, compareWith);
12828
12829 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12830}
12831
12832// Remove specified attributes from this object
12833bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12834{
12835 wxTextAttr::RemoveStyle(*this, attr);
12836
12837 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12838}
12839
12840// Collects the attributes that are common to a range of content, building up a note of
12841// which attributes are absent in some objects and which clash in some objects.
12842void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12843{
12844 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 12845
24777478
JS
12846 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12847}
12848
12849// Partial equality test
32423dd8 12850bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
24777478 12851{
32423dd8
JS
12852 if (!weakTest &&
12853 ((!HasStyle() && border.HasStyle()) ||
12854 (!HasColour() && border.HasColour()) ||
12855 (!HasWidth() && border.HasWidth())))
12856 {
12857 return false;
12858 }
12859
12860 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
24777478
JS
12861 return false;
12862
32423dd8 12863 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
24777478
JS
12864 return false;
12865
32423dd8 12866 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
24777478
JS
12867 return false;
12868
12869 return true;
12870}
12871
12872// Apply border to 'this', but not if the same as compareWith
bec80f4f 12873bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
12874{
12875 if (border.HasStyle())
12876 {
12877 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12878 SetStyle(border.GetStyle());
12879 }
12880 if (border.HasColour())
12881 {
12882 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12883 SetColour(border.GetColourLong());
12884 }
12885 if (border.HasWidth())
12886 {
12887 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12888 SetWidth(border.GetWidth());
12889 }
12890
12891 return true;
12892}
12893
12894// Remove specified attributes from this object
bec80f4f 12895bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
12896{
12897 if (attr.HasStyle() && HasStyle())
12898 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12899 if (attr.HasColour() && HasColour())
12900 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12901 if (attr.HasWidth() && HasWidth())
12902 m_borderWidth.Reset();
12903
12904 return true;
12905}
12906
12907// Collects the attributes that are common to a range of content, building up a note of
12908// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12909void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
12910{
12911 if (attr.HasStyle())
12912 {
12913 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12914 {
12915 if (HasStyle())
12916 {
12917 if (GetStyle() != attr.GetStyle())
12918 {
12919 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12920 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12921 }
12922 }
12923 else
12924 SetStyle(attr.GetStyle());
12925 }
12926 }
12927 else
12928 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12929
12930 if (attr.HasColour())
12931 {
12932 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12933 {
12934 if (HasColour())
12935 {
12936 if (GetColour() != attr.GetColour())
12937 {
12938 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12939 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12940 }
12941 }
12942 else
12943 SetColour(attr.GetColourLong());
12944 }
12945 }
12946 else
12947 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 12948
24777478
JS
12949 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12950}
12951
12952// Partial equality test
32423dd8 12953bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
24777478 12954{
32423dd8
JS
12955 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12956 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
24777478
JS
12957}
12958
12959// Apply border to 'this', but not if the same as compareWith
bec80f4f 12960bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 12961{
bec80f4f
JS
12962 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12963 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12964 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12965 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
12966 return true;
12967}
12968
12969// Remove specified attributes from this object
bec80f4f 12970bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
12971{
12972 m_left.RemoveStyle(attr.m_left);
12973 m_right.RemoveStyle(attr.m_right);
12974 m_top.RemoveStyle(attr.m_top);
12975 m_bottom.RemoveStyle(attr.m_bottom);
12976 return true;
12977}
12978
12979// Collects the attributes that are common to a range of content, building up a note of
12980// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12981void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
12982{
12983 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12984 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12985 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12986 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12987}
12988
12989// Set style of all borders
bec80f4f 12990void wxTextAttrBorders::SetStyle(int style)
24777478
JS
12991{
12992 m_left.SetStyle(style);
12993 m_right.SetStyle(style);
12994 m_top.SetStyle(style);
12995 m_bottom.SetStyle(style);
12996}
12997
12998// Set colour of all borders
bec80f4f 12999void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
13000{
13001 m_left.SetColour(colour);
13002 m_right.SetColour(colour);
13003 m_top.SetColour(colour);
13004 m_bottom.SetColour(colour);
13005}
13006
bec80f4f 13007void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
13008{
13009 m_left.SetColour(colour);
13010 m_right.SetColour(colour);
13011 m_top.SetColour(colour);
13012 m_bottom.SetColour(colour);
13013}
13014
13015// Set width of all borders
bec80f4f 13016void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
13017{
13018 m_left.SetWidth(width);
13019 m_right.SetWidth(width);
13020 m_top.SetWidth(width);
13021 m_bottom.SetWidth(width);
13022}
13023
13024// Partial equality test
32423dd8 13025bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
24777478 13026{
32423dd8
JS
13027 if (!weakTest && !IsValid() && dim.IsValid())
13028 return false;
13029
603f702b 13030 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
13031 return false;
13032 else
13033 return true;
13034}
13035
13036bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
13037{
603f702b 13038 if (dim.IsValid())
24777478
JS
13039 {
13040 if (!(compareWith && dim == (*compareWith)))
13041 (*this) = dim;
13042 }
13043
13044 return true;
13045}
13046
13047// Collects the attributes that are common to a range of content, building up a note of
13048// which attributes are absent in some objects and which clash in some objects.
13049void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
13050{
603f702b 13051 if (attr.IsValid())
24777478 13052 {
603f702b 13053 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 13054 {
603f702b 13055 if (IsValid())
24777478
JS
13056 {
13057 if (!((*this) == attr))
13058 {
603f702b
JS
13059 clashingAttr.SetValid(true);
13060 SetValid(false);
24777478
JS
13061 }
13062 }
13063 else
13064 (*this) = attr;
13065 }
13066 }
13067 else
603f702b 13068 absentAttr.SetValid(true);
24777478
JS
13069}
13070
8995db52
JS
13071wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
13072{
13073 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
13074}
13075
13076wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
13077{
13078 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
13079}
13080
bec80f4f
JS
13081int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
13082{
13083 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
13084}
13085
13086int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
13087{
13088 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
13089}
13090
13091int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
13092{
13093 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
13094 return ConvertTenthsMMToPixels(dim.GetValue());
13095 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
13096 return dim.GetValue();
13097 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
13098 {
13099 wxASSERT(m_parentSize != wxDefaultSize);
13100 if (direction == wxHORIZONTAL)
13101 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
13102 else
13103 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
13104 }
13105 else
13106 {
13107 wxASSERT(false);
13108 return 0;
13109 }
13110}
13111
13112int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
13113{
13114 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
13115 return dim.GetValue();
13116 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
13117 return ConvertPixelsToTenthsMM(dim.GetValue());
13118 else
13119 {
13120 wxASSERT(false);
13121 return 0;
13122 }
13123}
13124
24777478 13125// Partial equality test
32423dd8 13126bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
24777478 13127{
32423dd8 13128 if (!m_left.EqPartial(dims.m_left, weakTest))
24777478
JS
13129 return false;
13130
32423dd8 13131 if (!m_right.EqPartial(dims.m_right, weakTest))
24777478
JS
13132 return false;
13133
32423dd8 13134 if (!m_top.EqPartial(dims.m_top, weakTest))
24777478
JS
13135 return false;
13136
32423dd8 13137 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
24777478
JS
13138 return false;
13139
13140 return true;
13141}
13142
13143// Apply border to 'this', but not if the same as compareWith
bec80f4f 13144bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
13145{
13146 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
13147 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
13148 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
13149 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
13150
13151 return true;
13152}
13153
13154// Remove specified attributes from this object
bec80f4f 13155bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 13156{
603f702b 13157 if (attr.m_left.IsValid())
24777478 13158 m_left.Reset();
603f702b 13159 if (attr.m_right.IsValid())
24777478 13160 m_right.Reset();
603f702b 13161 if (attr.m_top.IsValid())
24777478 13162 m_top.Reset();
603f702b 13163 if (attr.m_bottom.IsValid())
24777478
JS
13164 m_bottom.Reset();
13165
13166 return true;
13167}
13168
13169// Collects the attributes that are common to a range of content, building up a note of
13170// which attributes are absent in some objects and which clash in some objects.
bec80f4f 13171void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
13172{
13173 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
13174 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
13175 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
13176 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
13177}
13178
603f702b 13179// Partial equality test
32423dd8 13180bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
603f702b 13181{
32423dd8 13182 if (!m_width.EqPartial(size.m_width, weakTest))
603f702b
JS
13183 return false;
13184
32423dd8 13185 if (!m_height.EqPartial(size.m_height, weakTest))
603f702b
JS
13186 return false;
13187
13188 return true;
13189}
13190
13191// Apply border to 'this', but not if the same as compareWith
13192bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
13193{
13194 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
13195 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
13196
13197 return true;
13198}
13199
13200// Remove specified attributes from this object
13201bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
13202{
13203 if (attr.m_width.IsValid())
13204 m_width.Reset();
13205 if (attr.m_height.IsValid())
13206 m_height.Reset();
13207
13208 return true;
13209}
13210
13211// Collects the attributes that are common to a range of content, building up a note of
13212// which attributes are absent in some objects and which clash in some objects.
13213void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
13214{
13215 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
13216 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
13217}
13218
24777478
JS
13219// Collects the attributes that are common to a range of content, building up a note of
13220// which attributes are absent in some objects and which clash in some objects.
13221void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
13222{
13223 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
13224 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
13225
13226 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
13227
c4168888
JS
13228 // If different font size units are being used, this is a clash.
13229 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
24777478 13230 {
c4168888
JS
13231 currentStyle.SetFontSize(0);
13232 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
13233 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
13234 }
13235 else
13236 {
13237 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
340ef5c5 13238 {
c4168888 13239 if (currentStyle.HasFontPointSize())
24777478 13240 {
c4168888 13241 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478 13242 {
c4168888
JS
13243 // Clash of attr - mark as such
13244 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
13245 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
13246 }
13247 }
c4168888
JS
13248 else
13249 currentStyle.SetFontSize(attr.GetFontSize());
13250 }
13251 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
13252 {
13253 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
13254 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
13255 }
13256
c4168888 13257 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
24777478 13258 {
c4168888 13259 if (currentStyle.HasFontPixelSize())
24777478 13260 {
c4168888 13261 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478
JS
13262 {
13263 // Clash of attr - mark as such
c4168888
JS
13264 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13265 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
24777478
JS
13266 }
13267 }
13268 else
c4168888 13269 currentStyle.SetFontPixelSize(attr.GetFontSize());
24777478 13270 }
c4168888
JS
13271 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
13272 {
13273 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13274 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13275 }
13276 }
24777478 13277
c4168888
JS
13278 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
13279 {
13280 if (currentStyle.HasFontItalic())
24777478 13281 {
c4168888 13282 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
24777478 13283 {
c4168888
JS
13284 // Clash of attr - mark as such
13285 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
13286 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
24777478 13287 }
24777478 13288 }
c4168888
JS
13289 else
13290 currentStyle.SetFontStyle(attr.GetFontStyle());
13291 }
13292 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
13293 {
13294 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
13295 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
13296 }
24777478 13297
c4168888
JS
13298 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
13299 {
13300 if (currentStyle.HasFontFamily())
24777478 13301 {
c4168888 13302 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
24777478 13303 {
c4168888
JS
13304 // Clash of attr - mark as such
13305 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
13306 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
24777478 13307 }
24777478 13308 }
c4168888
JS
13309 else
13310 currentStyle.SetFontFamily(attr.GetFontFamily());
13311 }
13312 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
13313 {
13314 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
13315 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
13316 }
24777478 13317
c4168888
JS
13318 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
13319 {
13320 if (currentStyle.HasFontWeight())
24777478 13321 {
c4168888 13322 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
24777478 13323 {
c4168888
JS
13324 // Clash of attr - mark as such
13325 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
13326 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
13327 }
13328 }
13329 else
13330 currentStyle.SetFontWeight(attr.GetFontWeight());
13331 }
13332 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
13333 {
13334 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
13335 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
13336 }
24777478 13337
c4168888
JS
13338 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
13339 {
13340 if (currentStyle.HasFontFaceName())
13341 {
13342 wxString faceName1(currentStyle.GetFontFaceName());
13343 wxString faceName2(attr.GetFontFaceName());
13344
13345 if (faceName1 != faceName2)
13346 {
13347 // Clash of attr - mark as such
13348 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13349 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
24777478 13350 }
24777478 13351 }
c4168888
JS
13352 else
13353 currentStyle.SetFontFaceName(attr.GetFontFaceName());
13354 }
13355 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
13356 {
13357 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13358 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13359 }
24777478 13360
c4168888
JS
13361 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
13362 {
13363 if (currentStyle.HasFontUnderlined())
24777478 13364 {
c4168888 13365 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
24777478 13366 {
c4168888
JS
13367 // Clash of attr - mark as such
13368 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13369 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
24777478 13370 }
24777478 13371 }
c4168888
JS
13372 else
13373 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
13374 }
13375 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
13376 {
13377 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13378 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13379 }
32423dd8 13380
c4168888
JS
13381 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
13382 {
13383 if (currentStyle.HasFontStrikethrough())
32423dd8 13384 {
c4168888 13385 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
32423dd8 13386 {
c4168888
JS
13387 // Clash of attr - mark as such
13388 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13389 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
32423dd8 13390 }
32423dd8 13391 }
c4168888
JS
13392 else
13393 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13394 }
13395 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13396 {
13397 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13398 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
24777478
JS
13399 }
13400
13401 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13402 {
13403 if (currentStyle.HasTextColour())
13404 {
13405 if (currentStyle.GetTextColour() != attr.GetTextColour())
13406 {
13407 // Clash of attr - mark as such
13408 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13409 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13410 }
13411 }
13412 else
13413 currentStyle.SetTextColour(attr.GetTextColour());
13414 }
c4168888
JS
13415 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13416 {
13417 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13418 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13419 }
24777478
JS
13420
13421 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13422 {
13423 if (currentStyle.HasBackgroundColour())
13424 {
13425 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13426 {
13427 // Clash of attr - mark as such
13428 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13429 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13430 }
13431 }
13432 else
13433 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13434 }
c4168888
JS
13435 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13436 {
13437 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13438 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13439 }
24777478
JS
13440
13441 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13442 {
13443 if (currentStyle.HasAlignment())
13444 {
13445 if (currentStyle.GetAlignment() != attr.GetAlignment())
13446 {
13447 // Clash of attr - mark as such
13448 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13449 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13450 }
13451 }
13452 else
13453 currentStyle.SetAlignment(attr.GetAlignment());
13454 }
c4168888
JS
13455 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13456 {
13457 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13458 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13459 }
24777478
JS
13460
13461 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13462 {
13463 if (currentStyle.HasTabs())
13464 {
13465 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13466 {
13467 // Clash of attr - mark as such
13468 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13469 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13470 }
13471 }
13472 else
13473 currentStyle.SetTabs(attr.GetTabs());
13474 }
c4168888
JS
13475 else if (!attr.HasTabs() && currentStyle.HasTabs())
13476 {
13477 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13478 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13479 }
24777478
JS
13480
13481 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13482 {
13483 if (currentStyle.HasLeftIndent())
13484 {
13485 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13486 {
13487 // Clash of attr - mark as such
13488 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13489 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13490 }
13491 }
13492 else
13493 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13494 }
c4168888
JS
13495 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13496 {
13497 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13498 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13499 }
24777478
JS
13500
13501 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13502 {
13503 if (currentStyle.HasRightIndent())
13504 {
13505 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13506 {
13507 // Clash of attr - mark as such
13508 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13509 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13510 }
13511 }
13512 else
13513 currentStyle.SetRightIndent(attr.GetRightIndent());
13514 }
c4168888
JS
13515 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13516 {
13517 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13518 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13519 }
24777478
JS
13520
13521 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13522 {
13523 if (currentStyle.HasParagraphSpacingAfter())
13524 {
13525 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13526 {
13527 // Clash of attr - mark as such
13528 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13529 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13530 }
13531 }
13532 else
13533 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13534 }
c4168888
JS
13535 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13536 {
13537 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13538 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13539 }
24777478
JS
13540
13541 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13542 {
13543 if (currentStyle.HasParagraphSpacingBefore())
13544 {
13545 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13546 {
13547 // Clash of attr - mark as such
13548 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13549 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13550 }
13551 }
13552 else
13553 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13554 }
c4168888
JS
13555 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13556 {
13557 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13558 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13559 }
24777478
JS
13560
13561 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13562 {
13563 if (currentStyle.HasLineSpacing())
13564 {
13565 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13566 {
13567 // Clash of attr - mark as such
13568 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13569 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13570 }
13571 }
13572 else
13573 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13574 }
c4168888
JS
13575 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13576 {
13577 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13578 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13579 }
24777478
JS
13580
13581 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13582 {
13583 if (currentStyle.HasCharacterStyleName())
13584 {
13585 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13586 {
13587 // Clash of attr - mark as such
13588 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13589 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13590 }
13591 }
13592 else
13593 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13594 }
c4168888
JS
13595 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13596 {
13597 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13598 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13599 }
24777478
JS
13600
13601 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13602 {
13603 if (currentStyle.HasParagraphStyleName())
13604 {
13605 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13606 {
13607 // Clash of attr - mark as such
13608 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13609 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13610 }
13611 }
13612 else
13613 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13614 }
c4168888
JS
13615 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13616 {
13617 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13618 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13619 }
24777478
JS
13620
13621 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13622 {
13623 if (currentStyle.HasListStyleName())
13624 {
13625 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13626 {
13627 // Clash of attr - mark as such
13628 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13629 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13630 }
13631 }
13632 else
13633 currentStyle.SetListStyleName(attr.GetListStyleName());
13634 }
c4168888
JS
13635 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13636 {
13637 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13638 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13639 }
24777478
JS
13640
13641 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13642 {
13643 if (currentStyle.HasBulletStyle())
13644 {
13645 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13646 {
13647 // Clash of attr - mark as such
13648 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13649 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13650 }
13651 }
13652 else
13653 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13654 }
c4168888
JS
13655 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13656 {
13657 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13658 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13659 }
24777478
JS
13660
13661 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13662 {
13663 if (currentStyle.HasBulletNumber())
13664 {
13665 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13666 {
13667 // Clash of attr - mark as such
13668 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13669 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13670 }
13671 }
13672 else
13673 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13674 }
c4168888
JS
13675 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13676 {
13677 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13678 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13679 }
24777478
JS
13680
13681 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13682 {
13683 if (currentStyle.HasBulletText())
13684 {
13685 if (currentStyle.GetBulletText() != attr.GetBulletText())
13686 {
13687 // Clash of attr - mark as such
13688 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13689 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13690 }
13691 }
13692 else
13693 {
13694 currentStyle.SetBulletText(attr.GetBulletText());
13695 currentStyle.SetBulletFont(attr.GetBulletFont());
13696 }
13697 }
c4168888
JS
13698 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13699 {
13700 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13701 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13702 }
24777478
JS
13703
13704 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13705 {
13706 if (currentStyle.HasBulletName())
13707 {
13708 if (currentStyle.GetBulletName() != attr.GetBulletName())
13709 {
13710 // Clash of attr - mark as such
13711 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13712 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13713 }
13714 }
13715 else
13716 {
13717 currentStyle.SetBulletName(attr.GetBulletName());
13718 }
13719 }
c4168888
JS
13720 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13721 {
13722 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13723 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13724 }
24777478
JS
13725
13726 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13727 {
13728 if (currentStyle.HasURL())
13729 {
13730 if (currentStyle.GetURL() != attr.GetURL())
13731 {
13732 // Clash of attr - mark as such
13733 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13734 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13735 }
13736 }
13737 else
13738 {
13739 currentStyle.SetURL(attr.GetURL());
13740 }
13741 }
c4168888
JS
13742 else if (!attr.HasURL() && currentStyle.HasURL())
13743 {
13744 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13745 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13746 }
24777478
JS
13747
13748 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13749 {
13750 if (currentStyle.HasTextEffects())
13751 {
13752 // We need to find the bits in the new attr that are different:
13753 // just look at those bits that are specified by the new attr.
13754
13755 // We need to remove the bits and flags that are not common between current attr
13756 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13757 // previous styles.
13758
13759 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13760 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13761
13762 if (currentRelevantTextEffects != newRelevantTextEffects)
13763 {
13764 // Find the text effects that were different, using XOR
13765 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13766
13767 // Clash of attr - mark as such
13768 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13769 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13770 }
13771 }
13772 else
13773 {
13774 currentStyle.SetTextEffects(attr.GetTextEffects());
13775 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13776 }
13777
13778 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13779 // that we've looked at so far
13780 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13781 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13782
13783 if (currentStyle.GetTextEffectFlags() == 0)
13784 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13785 }
c4168888
JS
13786 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13787 {
13788 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13789 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13790 }
24777478
JS
13791
13792 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13793 {
13794 if (currentStyle.HasOutlineLevel())
13795 {
13796 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13797 {
13798 // Clash of attr - mark as such
13799 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13800 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13801 }
13802 }
13803 else
13804 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13805 }
c4168888
JS
13806 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13807 {
13808 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13809 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13810 }
24777478
JS
13811}
13812
bec80f4f
JS
13813WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13814
f7667b84
JS
13815// JACS 2013-01-27
13816WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13817
bec80f4f
JS
13818IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13819
13820bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13821{
13822 if (m_properties.GetCount() != props.GetCount())
13823 return false;
13824
13825 size_t i;
13826 for (i = 0; i < m_properties.GetCount(); i++)
13827 {
13828 const wxVariant& var1 = m_properties[i];
13829 int idx = props.Find(var1.GetName());
13830 if (idx == -1)
13831 return false;
13832 const wxVariant& var2 = props.m_properties[idx];
13833 if (!(var1 == var2))
13834 return false;
13835 }
13836
13837 return true;
13838}
13839
13840wxArrayString wxRichTextProperties::GetPropertyNames() const
13841{
13842 wxArrayString arr;
13843 size_t i;
13844 for (i = 0; i < m_properties.GetCount(); i++)
13845 {
13846 arr.Add(m_properties[i].GetName());
13847 }
13848 return arr;
13849}
13850
13851int wxRichTextProperties::Find(const wxString& name) const
13852{
13853 size_t i;
13854 for (i = 0; i < m_properties.GetCount(); i++)
13855 {
13856 if (m_properties[i].GetName() == name)
13857 return (int) i;
13858 }
13859 return -1;
13860}
13861
590a0f8b
JS
13862bool wxRichTextProperties::Remove(const wxString& name)
13863{
13864 int idx = Find(name);
13865 if (idx != -1)
13866 {
13867 m_properties.RemoveAt(idx);
13868 return true;
13869 }
13870 else
13871 return false;
13872}
13873
bec80f4f
JS
13874wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13875{
13876 int idx = Find(name);
13877 if (idx == wxNOT_FOUND)
13878 SetProperty(name, wxString());
13879 idx = Find(name);
13880 if (idx != wxNOT_FOUND)
13881 {
13882 return & (*this)[idx];
13883 }
13884 else
13885 return NULL;
13886}
13887
13888const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13889{
13890 static const wxVariant nullVariant;
13891 int idx = Find(name);
13892 if (idx != -1)
13893 return m_properties[idx];
13894 else
13895 return nullVariant;
13896}
13897
13898wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13899{
13900 return GetProperty(name).GetString();
13901}
13902
13903long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13904{
13905 return GetProperty(name).GetLong();
13906}
13907
13908bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13909{
13910 return GetProperty(name).GetBool();
13911}
13912
13913double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13914{
13915 return GetProperty(name).GetDouble();
13916}
13917
13918void wxRichTextProperties::SetProperty(const wxVariant& variant)
13919{
13920 wxASSERT(!variant.GetName().IsEmpty());
13921
13922 int idx = Find(variant.GetName());
13923
13924 if (idx == -1)
13925 m_properties.Add(variant);
13926 else
13927 m_properties[idx] = variant;
13928}
13929
13930void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13931{
13932 int idx = Find(name);
13933 wxVariant var(variant);
13934 var.SetName(name);
13935
13936 if (idx == -1)
13937 m_properties.Add(var);
13938 else
13939 m_properties[idx] = var;
13940}
13941
13942void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13943{
13944 SetProperty(name, wxVariant(value, name));
13945}
13946
13947void wxRichTextProperties::SetProperty(const wxString& name, long value)
13948{
13949 SetProperty(name, wxVariant(value, name));
13950}
13951
13952void wxRichTextProperties::SetProperty(const wxString& name, double value)
13953{
13954 SetProperty(name, wxVariant(value, name));
13955}
13956
13957void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13958{
13959 SetProperty(name, wxVariant(value, name));
13960}
24777478 13961
590a0f8b
JS
13962void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13963{
13964 size_t i;
13965 for (i = 0; i < properties.GetCount(); i++)
13966 {
13967 wxString name = properties.GetProperties()[i].GetName();
13968 if (HasProperty(name))
13969 Remove(name);
13970 }
13971}
13972
13973void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13974{
13975 size_t i;
13976 for (i = 0; i < properties.GetCount(); i++)
13977 {
13978 SetProperty(properties.GetProperties()[i]);
13979 }
13980}
13981
603f702b
JS
13982wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13983{
13984 if (m_address.GetCount() == 0)
13985 return topLevelContainer;
13986
13987 wxRichTextCompositeObject* p = topLevelContainer;
13988 size_t i = 0;
13989 while (p && i < m_address.GetCount())
13990 {
13991 int pos = m_address[i];
13992 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13993 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13994 return NULL;
13995
13996 wxRichTextObject* p1 = p->GetChild(pos);
13997 if (i == (m_address.GetCount()-1))
13998 return p1;
13999
14000 p = wxDynamicCast(p1, wxRichTextCompositeObject);
14001 i ++;
14002 }
14003 return NULL;
14004}
14005
14006bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
14007{
14008 m_address.Clear();
14009
14010 if (topLevelContainer == obj)
14011 return true;
14012
14013 wxRichTextObject* o = obj;
14014 while (o)
14015 {
14016 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
14017 if (!p)
14018 return false;
14019
14020 int pos = p->GetChildren().IndexOf(o);
14021 if (pos == -1)
14022 return false;
14023
14024 m_address.Insert(pos, 0);
14025
14026 if (p == topLevelContainer)
14027 return true;
14028
14029 o = p;
14030 }
14031 return false;
14032}
14033
14034// Equality test
14035bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
14036{
14037 if (m_container != sel.m_container)
14038 return false;
14039 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
14040 return false;
14041 size_t i;
14042 for (i = 0; i < m_ranges.GetCount(); i++)
14043 if (!(m_ranges[i] == sel.m_ranges[i]))
14044 return false;
14045 return true;
14046}
14047
14048// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
14049// or none at the level of the object's container.
14050wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
14051{
14052 if (IsValid())
14053 {
14054 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
14055
14056 if (container == m_container)
14057 return m_ranges;
14058
14059 container = obj->GetContainer();
14060 while (container)
14061 {
14062 if (container->GetParent())
14063 {
14064 // If we found that our object's container is within the range of
14065 // a selection higher up, then assume the whole original object
14066 // is also selected.
14067 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
14068 if (parentContainer == m_container)
14069 {
14070 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
14071 {
14072 wxRichTextRangeArray ranges;
14073 ranges.Add(obj->GetRange());
14074 return ranges;
14075 }
14076 }
14077
14078 container = parentContainer;
14079 }
14080 else
14081 {
14082 container = NULL;
14083 break;
14084 }
14085 }
14086 }
14087 return wxRichTextRangeArray();
14088}
14089
14090// Is the given position within the selection?
14091bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
14092{
14093 if (!IsValid())
14094 return false;
14095 else
14096 {
14097 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
14098 return WithinSelection(pos, selectionRanges);
14099 }
14100}
14101
14102// Is the given position within the selection range?
14103bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
14104{
14105 size_t i;
14106 for (i = 0; i < ranges.GetCount(); i++)
14107 {
14108 const wxRichTextRange& range = ranges[i];
14109 if (pos >= range.GetStart() && pos <= range.GetEnd())
14110 return true;
14111 }
14112 return false;
14113}
14114
14115// Is the given range completely within the selection range?
14116bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
14117{
14118 size_t i;
14119 for (i = 0; i < ranges.GetCount(); i++)
14120 {
14121 const wxRichTextRange& eachRange = ranges[i];
14122 if (range.IsWithin(eachRange))
14123 return true;
14124 }
14125 return false;
14126}
14127
8db2e3ef
JS
14128IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
14129IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
14130
f7667b84
JS
14131wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
14132{
14133 Init();
14134 m_buffer = buffer;
14135 if (m_buffer && m_buffer->GetRichTextCtrl())
14136 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
14137}
14138
8db2e3ef
JS
14139bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
14140{
f7667b84
JS
14141 if (!GetVirtualAttributesEnabled())
14142 return false;
14143
8db2e3ef
JS
14144 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14145 while (node)
14146 {
14147 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14148 if (handler->HasVirtualAttributes(obj))
14149 return true;
14150
14151 node = node->GetNext();
14152 }
14153 return false;
14154}
14155
14156wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
14157{
14158 wxRichTextAttr attr;
f7667b84
JS
14159 if (!GetVirtualAttributesEnabled())
14160 return attr;
14161
8db2e3ef
JS
14162 // We apply all handlers, so we can may combine several different attributes
14163 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14164 while (node)
14165 {
14166 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14167 if (handler->HasVirtualAttributes(obj))
14168 {
14169 bool success = handler->GetVirtualAttributes(attr, obj);
14170 wxASSERT(success);
aa8f57f4 14171 wxUnusedVar(success);
8db2e3ef
JS
14172 }
14173
14174 node = node->GetNext();
14175 }
14176 return attr;
14177}
14178
14179bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
14180{
f7667b84
JS
14181 if (!GetVirtualAttributesEnabled())
14182 return false;
14183
8db2e3ef
JS
14184 if (HasVirtualAttributes(obj))
14185 {
14186 wxRichTextAttr a(GetVirtualAttributes(obj));
14187 attr.Apply(a);
14188 return true;
14189 }
14190 else
14191 return false;
14192}
14193
f7667b84
JS
14194int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
14195{
14196 if (!GetVirtualAttributesEnabled())
14197 return 0;
14198
14199 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14200 while (node)
14201 {
14202 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14203 int count = handler->GetVirtualSubobjectAttributesCount(obj);
14204 if (count > 0)
14205 return count;
14206
14207 node = node->GetNext();
14208 }
14209 return 0;
14210}
14211
14212int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
14213{
14214 if (!GetVirtualAttributesEnabled())
14215 return 0;
14216
14217 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14218 while (node)
14219 {
14220 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14221 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
14222 return positions.GetCount();
14223
14224 node = node->GetNext();
14225 }
14226 return 0;
14227}
14228
14229bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
14230{
14231 if (!GetVirtualAttributesEnabled())
14232 return false;
14233
14234 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14235 while (node)
14236 {
14237 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14238 if (handler->HasVirtualText(obj))
14239 return true;
14240
14241 node = node->GetNext();
14242 }
14243 return false;
14244}
14245
14246bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
14247{
14248 if (!GetVirtualAttributesEnabled())
14249 return false;
14250
14251 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14252 while (node)
14253 {
14254 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14255 if (handler->GetVirtualText(obj, text))
14256 return true;
14257
14258 node = node->GetNext();
14259 }
14260 return false;
14261}
14262
8db2e3ef
JS
14263/// Adds a handler to the end
14264void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
14265{
14266 sm_drawingHandlers.Append(handler);
14267}
14268
14269/// Inserts a handler at the front
14270void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
14271{
14272 sm_drawingHandlers.Insert( handler );
14273}
14274
14275/// Removes a handler
14276bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
14277{
14278 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
14279 if (handler)
14280 {
14281 sm_drawingHandlers.DeleteObject(handler);
14282 delete handler;
14283 return true;
14284 }
14285 else
14286 return false;
14287}
14288
14289wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
14290{
14291 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
14292 while (node)
14293 {
14294 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14295 if (handler->GetName().Lower() == name.Lower()) return handler;
14296
14297 node = node->GetNext();
14298 }
14299 return NULL;
14300}
14301
14302void wxRichTextBuffer::CleanUpDrawingHandlers()
14303{
14304 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
14305 while (node)
14306 {
14307 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
14308 wxList::compatibility_iterator next = node->GetNext();
14309 delete handler;
14310 node = next;
14311 }
14312
14313 sm_drawingHandlers.Clear();
14314}
603f702b 14315
7c9fdebe
JS
14316void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
14317{
14318 sm_fieldTypes[fieldType->GetName()] = fieldType;
14319}
14320
14321bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
14322{
14323 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
14324 if (it == sm_fieldTypes.end())
14325 return false;
14326 else
14327 {
14328 wxRichTextFieldType* fieldType = it->second;
14329 sm_fieldTypes.erase(it);
14330 delete fieldType;
14331 return true;
14332 }
14333}
14334
14335wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
14336{
14337 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
14338 if (it == sm_fieldTypes.end())
14339 return NULL;
14340 else
14341 return it->second;
14342}
14343
14344void wxRichTextBuffer::CleanUpFieldTypes()
14345{
14346 wxRichTextFieldTypeHashMap::iterator it;
14347 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
14348 {
14349 wxRichTextFieldType* fieldType = it->second;
14350 delete fieldType;
14351 }
14352
14353 sm_fieldTypes.clear();
14354}
14355
5d7836c4
JS
14356#endif
14357 // wxUSE_RICHTEXT