]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
No real changes, just use wxString::clear() instead of assignment.
[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();
1450 wxRichTextObjectList::compatibility_iterator nextNode = node->GetNext();
1451
1452 // First split child and nextChild so we have smaller fragments to merge.
1453 // Then Merge only has to test per-object virtual attributes
1454 // because for an object with all the same sub-object attributes,
1455 // then any general virtual attributes should be merged with sub-objects by
1456 // the implementation.
1457
1458 wxRichTextObject* nextChildAfterSplit = nextChild;
1459
1460 if (nextChildAfterSplit->CanSplit(context))
1461 nextChildAfterSplit = nextChild->Split(context);
1462
1463 bool splitNextChild = nextChild != nextChildAfterSplit;
1464
1465 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1466 // Note that we use nextChild because if we had split nextChild, the first object always
1467 // remains (and further parts are appended). However we must use childAfterSplit since
1468 // it's the last part of a possibly split child.
1469
1470 if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
1471 {
1472 nextChild->Dereference();
1473 m_children.Erase(node->GetNext());
1474
1475 // Don't set node -- we'll see if we can merge again with the next
1476 // child. UNLESS we split this or the next child, in which case we know we have to
1477 // move on to the end of the next child.
1478 if (splitNextChild)
1479 node = m_children.Find(nextChildAfterSplit);
1480 }
1481 else
1482 {
1483 if (splitNextChild)
1484 node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
1485 else
1486 node = node->GetNext();
1487 }
1488 }
1489 else
1490 node = node->GetNext();
1491 }
5d7836c4
JS
1492 }
1493 else
1494 node = node->GetNext();
1495 }
1496
bec80f4f
JS
1497 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1498 if (GetChildCount() > 1)
5d7836c4 1499 {
bec80f4f
JS
1500 node = m_children.GetFirst();
1501 while (node)
1502 {
1503 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1504 wxRichTextObject* child = node->GetData();
1505 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1506 {
1507 if (child->IsEmpty())
1508 {
1509 child->Dereference();
1510 m_children.Erase(node);
1511 }
1512 node = next;
1513 }
1514 else
1515 node = node->GetNext();
1516 }
5d7836c4 1517 }
5d7836c4 1518
5d7836c4
JS
1519 return true;
1520}
1521
bec80f4f
JS
1522/// Dump to output stream for debugging
1523void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1524{
1525 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1526 while (node)
1527 {
1528 wxRichTextObject* child = node->GetData();
bec80f4f 1529 child->Dump(stream);
5d7836c4
JS
1530 node = node->GetNext();
1531 }
5d7836c4
JS
1532}
1533
603f702b
JS
1534/// Get/set the object size for the given range. Returns false if the range
1535/// is invalid for this object.
914a4e23 1536bool 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
1537{
1538 if (!range.IsWithin(GetRange()))
1539 return false;
5d7836c4 1540
603f702b 1541 wxSize sz;
5d7836c4 1542
603f702b
JS
1543 wxArrayInt childExtents;
1544 wxArrayInt* p;
1545 if (partialExtents)
1546 p = & childExtents;
1547 else
1548 p = NULL;
5d7836c4 1549
603f702b
JS
1550 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1551 while (node)
cdaed652 1552 {
603f702b
JS
1553 wxRichTextObject* child = node->GetData();
1554 if (!child->GetRange().IsOutside(range))
1555 {
1556 // Floating objects have a zero size within the paragraph.
e12b91a3 1557 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
603f702b
JS
1558 {
1559 if (partialExtents)
1560 {
1561 int lastSize;
1562 if (partialExtents->GetCount() > 0)
1563 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1564 else
1565 lastSize = 0;
cdaed652 1566
603f702b
JS
1567 partialExtents->Add(0 /* zero size */ + lastSize);
1568 }
1569 }
1570 else
1571 {
1572 wxSize childSize;
5d7836c4 1573
603f702b
JS
1574 wxRichTextRange rangeToUse = range;
1575 rangeToUse.LimitTo(child->GetRange());
1576 if (child->IsTopLevel())
1577 rangeToUse = child->GetOwnRange();
5d7836c4 1578
603f702b 1579 int childDescent = 0;
cdaed652 1580
603f702b
JS
1581 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1582 // but it's only going to be used after caching has taken place.
1583 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1584 {
1585 childDescent = child->GetDescent();
1586 childSize = child->GetCachedSize();
bec80f4f 1587
603f702b
JS
1588 sz.y = wxMax(sz.y, childSize.y);
1589 sz.x += childSize.x;
1590 descent = wxMax(descent, childDescent);
1591 }
914a4e23 1592 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
603f702b
JS
1593 {
1594 sz.y = wxMax(sz.y, childSize.y);
1595 sz.x += childSize.x;
1596 descent = wxMax(descent, childDescent);
bec80f4f 1597
603f702b
JS
1598 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1599 {
1600 child->SetCachedSize(childSize);
1601 child->SetDescent(childDescent);
1602 }
bec80f4f 1603
603f702b
JS
1604 if (partialExtents)
1605 {
1606 int lastSize;
1607 if (partialExtents->GetCount() > 0)
1608 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1609 else
1610 lastSize = 0;
bec80f4f 1611
603f702b
JS
1612 size_t i;
1613 for (i = 0; i < childExtents.GetCount(); i++)
1614 {
1615 partialExtents->Add(childExtents[i] + lastSize);
1616 }
1617 }
1618 }
1619 }
1620
1621 if (p)
1622 p->Clear();
1623 }
1624
1625 node = node->GetNext();
1626 }
1627 size = sz;
1628 return true;
1629}
1630
1631// Invalidate the buffer. With no argument, invalidates whole buffer.
1632void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1633{
1634 wxRichTextObject::Invalidate(invalidRange);
1635
1636 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1637 while (node)
1638 {
1639 wxRichTextObject* child = node->GetData();
1640 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1641 {
1642 // Skip
1643 }
1644 else if (child->IsTopLevel())
1645 {
e12b91a3 1646 if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
c4168888
JS
1647 {
1648 // Don't invalidate subhierarchy if we've already been laid out
1649 }
603f702b 1650 else
c4168888
JS
1651 {
1652 if (invalidRange == wxRICHTEXT_NONE)
1653 child->Invalidate(wxRICHTEXT_NONE);
1654 else
1655 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1656 }
603f702b
JS
1657 }
1658 else
1659 child->Invalidate(invalidRange);
1660 node = node->GetNext();
1661 }
1662}
1663
1664// Move the object recursively, by adding the offset from old to new
1665void wxRichTextCompositeObject::Move(const wxPoint& pt)
1666{
1667 wxPoint oldPos = GetPosition();
1668 SetPosition(pt);
1669 wxPoint offset = pt - oldPos;
1670
1671 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1672 while (node)
1673 {
1674 wxRichTextObject* child = node->GetData();
1675 wxPoint childPos = child->GetPosition() + offset;
1676 child->Move(childPos);
1677 node = node->GetNext();
1678 }
1679}
1680
1681
1682/*!
1683 * wxRichTextParagraphLayoutBox
1684 * This box knows how to lay out paragraphs.
1685 */
1686
1687IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1688
1689wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1690 wxRichTextCompositeObject(parent)
1691{
1692 Init();
1693}
1694
1695wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1696{
1697 if (m_floatCollector)
1698 {
1699 delete m_floatCollector;
1700 m_floatCollector = NULL;
1701 }
1702}
1703
1704/// Initialize the object.
1705void wxRichTextParagraphLayoutBox::Init()
1706{
1707 m_ctrl = NULL;
1708
1709 // For now, assume is the only box and has no initial size.
1710 m_range = wxRichTextRange(0, -1);
1711 m_ownRange = wxRichTextRange(0, -1);
1712
1713 m_invalidRange = wxRICHTEXT_ALL;
1714
603f702b
JS
1715 m_partialParagraph = false;
1716 m_floatCollector = NULL;
1717}
1718
1719void wxRichTextParagraphLayoutBox::Clear()
1720{
1721 DeleteChildren();
1722
1723 if (m_floatCollector)
1724 delete m_floatCollector;
1725 m_floatCollector = NULL;
1726 m_partialParagraph = false;
1727}
1728
1729/// Copy
1730void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1731{
1732 Clear();
1733
1734 wxRichTextCompositeObject::Copy(obj);
1735
1736 m_partialParagraph = obj.m_partialParagraph;
1737 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1738}
1739
07d4142f
JS
1740// Gather information about floating objects; only gather floats for those paragraphs that
1741// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1742// during layout.
603f702b 1743bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1744{
1745 if (m_floatCollector != NULL)
1746 delete m_floatCollector;
603f702b 1747 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652 1748 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
07d4142f
JS
1749 // Only gather floats up to the point we'll start formatting paragraphs.
1750 while (untilObj && node && node->GetData() != untilObj)
cdaed652
VZ
1751 {
1752 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1753 wxASSERT (child != NULL);
1754 if (child)
1755 m_floatCollector->CollectFloat(child);
1756 node = node->GetNext();
1757 }
ce00f59b 1758
cdaed652
VZ
1759 return true;
1760}
1761
603f702b
JS
1762// Returns the style sheet associated with the overall buffer.
1763wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1764{
1765 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1766}
1767
1768// Get the number of floating objects at this level
1769int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1770{
1771 if (m_floatCollector)
1772 return m_floatCollector->GetFloatingObjectCount();
1773 else
1774 return 0;
1775}
1776
1777// Get a list of floating objects
1778bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1779{
1780 if (m_floatCollector)
1781 {
1782 return m_floatCollector->GetFloatingObjects(objects);
1783 }
1784 else
1785 return false;
1786}
1787
1788// Calculate ranges
1789void wxRichTextParagraphLayoutBox::UpdateRanges()
1790{
1791 long start = 0;
1792 if (GetParent())
1793 start = GetRange().GetStart();
1794 long end;
1795 CalculateRange(start, end);
1796}
1797
cdaed652 1798// HitTest
8db2e3ef 1799int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1800{
603f702b
JS
1801 if (!IsShown())
1802 return wxRICHTEXT_HITTEST_NONE;
1803
cdaed652 1804 int ret = wxRICHTEXT_HITTEST_NONE;
e12b91a3 1805 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
8db2e3ef 1806 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
ce00f59b 1807
cdaed652 1808 if (ret == wxRICHTEXT_HITTEST_NONE)
8db2e3ef 1809 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
cdaed652 1810 else
603f702b
JS
1811 {
1812 *contextObj = this;
cdaed652 1813 return ret;
603f702b 1814 }
cdaed652
VZ
1815}
1816
1817/// Draw the floating objects
8db2e3ef 1818void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652 1819{
e12b91a3 1820 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
8db2e3ef 1821 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
cdaed652
VZ
1822}
1823
bec80f4f 1824void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1825{
1826 if (from == to)
1827 return;
1828
1829 from->RemoveChild(obj);
1830 to->AppendChild(obj);
5d7836c4
JS
1831}
1832
1833/// Draw the item
8db2e3ef 1834bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1835{
603f702b
JS
1836 if (!IsShown())
1837 return true;
1838
1839 wxRect thisRect(GetPosition(), GetCachedSize());
1840
8db2e3ef
JS
1841 wxRichTextAttr attr(GetAttributes());
1842 context.ApplyVirtualAttributes(attr, this);
1843
603f702b
JS
1844 int flags = style;
1845 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1846 flags |= wxRICHTEXT_DRAW_SELECTED;
1847
1848 // Don't draw guidelines if at top level
1849 int theseFlags = flags;
1850 if (!GetParent())
1851 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
8db2e3ef 1852 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
603f702b 1853
e12b91a3
JS
1854 if (wxRichTextBuffer::GetFloatingLayoutMode())
1855 DrawFloats(dc, context, range, selection, rect, descent, style);
1856
5d7836c4
JS
1857 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1858 while (node)
1859 {
603f702b 1860 wxRichTextObject* child = node->GetData();
7fe8059f 1861
5d7836c4
JS
1862 if (child && !child->GetRange().IsOutside(range))
1863 {
1864 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1865 wxRichTextRange childRange = range;
1866 if (child->IsTopLevel())
1867 {
1868 childRange = child->GetOwnRange();
1869 }
7fe8059f 1870
ea160b2e
JS
1871 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1872 {
1873 // Stop drawing
1874 break;
1875 }
1876 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1877 {
1878 // Skip
1879 }
1880 else
8db2e3ef 1881 child->Draw(dc, context, childRange, selection, rect, descent, style);
5d7836c4
JS
1882 }
1883
1884 node = node->GetNext();
1885 }
1886 return true;
1887}
1888
1889/// Lay the item out
8db2e3ef 1890bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 1891{
603f702b
JS
1892 SetPosition(rect.GetPosition());
1893
1894 if (!IsShown())
1895 return true;
1896
4d551ad5
JS
1897 wxRect availableSpace;
1898 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1899
8db2e3ef
JS
1900 wxRichTextAttr attr(GetAttributes());
1901 context.ApplyVirtualAttributes(attr, this);
1902
4d551ad5 1903 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1904 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1905 // so that during a size, only the visible part will be relaid out, or
1906 // it would take too long causing flicker. As an approximation, we assume that
1907 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1908 if (formatRect)
1909 {
603f702b 1910 wxRect rect2(0, 0, rect.width, rect.height);
8db2e3ef 1911 availableSpace = GetAvailableContentArea(dc, context, rect2);
4d551ad5
JS
1912
1913 // Invalidate the part of the buffer from the first visible line
1914 // to the end. If other parts of the buffer are currently invalid,
1915 // then they too will be taken into account if they are above
1916 // the visible point.
1917 long startPos = 0;
1918 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1919 if (line)
1920 startPos = line->GetAbsoluteRange().GetStart();
1921
603f702b 1922 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1923 }
1924 else
603f702b 1925 {
8db2e3ef 1926 availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
1927 }
1928
d157d142
JS
1929 // Fix the width if we're at the top level
1930 if (!GetParent())
1931 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1932
603f702b 1933 int leftMargin, rightMargin, topMargin, bottomMargin;
8db2e3ef 1934 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
603f702b 1935 topMargin, bottomMargin);
5d7836c4
JS
1936
1937 int maxWidth = 0;
603f702b
JS
1938 int maxHeight = 0;
1939
1940 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1941 int maxMaxWidth = 0;
1942
1943 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1944 int maxMinWidth = 0;
1945
1946 // If we have vertical alignment, we must recalculate everything.
8db2e3ef
JS
1947 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1948 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1949
5d7836c4 1950 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1951
38113684 1952 bool layoutAll = true;
1e967276 1953
38113684
JS
1954 // Get invalid range, rounding to paragraph start/end.
1955 wxRichTextRange invalidRange = GetInvalidRange(true);
1956
4d551ad5 1957 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1958 return true;
1959
603f702b 1960 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1961 layoutAll = true;
38113684 1962 else // If we know what range is affected, start laying out from that point on.
603f702b 1963 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1964 {
38113684 1965 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1966 if (firstParagraph)
1967 {
1968 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1969 wxRichTextObjectList::compatibility_iterator previousNode;
1970 if ( firstNode )
1971 previousNode = firstNode->GetPrevious();
9b4af7b7 1972 if (firstNode)
2c375f42 1973 {
9b4af7b7
JS
1974 if (previousNode)
1975 {
1976 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1977 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1978 }
7fe8059f 1979
2c375f42
JS
1980 // Now we're going to start iterating from the first affected paragraph.
1981 node = firstNode;
1e967276
JS
1982
1983 layoutAll = false;
2c375f42
JS
1984 }
1985 }
1986 }
1987
07d4142f
JS
1988 // Gather information about only those floating objects that will not be formatted,
1989 // after which floats will be gathered per-paragraph during layout.
e12b91a3
JS
1990 if (wxRichTextBuffer::GetFloatingLayoutMode())
1991 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1992
4d551ad5
JS
1993 // A way to force speedy rest-of-buffer layout (the 'else' below)
1994 bool forceQuickLayout = false;
39a1c2f2 1995
d3f6b1b5
JS
1996 // First get the size of the paragraphs we won't be laying out
1997 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1998 while (n && n != node)
1999 {
2000 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
2001 if (child)
2002 {
2003 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2004 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2005 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2006 }
2007 n = n->GetNext();
2008 }
2009
5d7836c4
JS
2010 while (node)
2011 {
2012 // Assume this box only contains paragraphs
2013
2014 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
706465df
JS
2015 // Unsure if this is needed
2016 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 2017
603f702b 2018 if (child && child->IsShown())
2c375f42 2019 {
603f702b
JS
2020 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2021 if ( !forceQuickLayout &&
2022 (layoutAll ||
2023 child->GetLines().IsEmpty() ||
2024 !child->GetRange().IsOutside(invalidRange)) )
2025 {
2026 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2027 // lays out the object again using the minimum size
8db2e3ef
JS
2028 child->LayoutToBestSize(dc, context, GetBuffer(),
2029 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2030
2031 // Layout must set the cached size
2032 availableSpace.y += child->GetCachedSize().y;
2033 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2034 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2035 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2036
2037 // If we're just formatting the visible part of the buffer,
2038 // and we're now past the bottom of the window, and we don't have any
2039 // floating objects (since they may cause wrapping to change for the rest of the
2040 // the buffer), start quick layout.
2041 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
2042 forceQuickLayout = true;
2043 }
2044 else
2045 {
2046 // We're outside the immediately affected range, so now let's just
2047 // move everything up or down. This assumes that all the children have previously
2048 // been laid out and have wrapped line lists associated with them.
2049 // TODO: check all paragraphs before the affected range.
2050
2051 int inc = availableSpace.y - child->GetPosition().y;
2052
2053 while (node)
2054 {
2055 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2056 if (child)
2057 {
2058 if (child->GetLines().GetCount() == 0)
2059 {
2060 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2061 // lays out the object again using the minimum size
8db2e3ef
JS
2062 child->LayoutToBestSize(dc, context, GetBuffer(),
2063 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2064
2065 //child->Layout(dc, availableChildRect, style);
2066 }
2067 else
2068 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 2069
603f702b
JS
2070 availableSpace.y += child->GetCachedSize().y;
2071 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2072 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2073 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2074 }
4d551ad5 2075
603f702b
JS
2076 node = node->GetNext();
2077 }
2078 break;
2079 }
2c375f42 2080 }
7fe8059f 2081
603f702b
JS
2082 node = node->GetNext();
2083 }
2084
2085 node = m_children.GetLast();
2086 if (node && node->GetData()->IsShown())
2087 {
2088 wxRichTextObject* child = node->GetData();
603f702b
JS
2089 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2090 }
2091 else
2092 maxHeight = 0; // topMargin + bottomMargin;
2093
23698b12 2094 // Check the bottom edge of any floating object
e12b91a3 2095 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
23698b12
JS
2096 {
2097 int bottom = GetFloatCollector()->GetLastRectBottom();
2098 if (bottom > maxHeight)
2099 maxHeight = bottom;
2100 }
2101
8db2e3ef 2102 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
bb7bbd12 2103 {
8db2e3ef 2104 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
bb7bbd12
JS
2105 int w = r.GetWidth();
2106
2107 // Convert external to content rect
2108 w = w - leftMargin - rightMargin;
2109 maxWidth = wxMax(maxWidth, w);
2110 maxMaxWidth = wxMax(maxMaxWidth, w);
2111 }
32423dd8
JS
2112 else
2113 {
2114 // TODO: Make sure the layout box's position reflects
2115 // the position of the children, but without
2116 // breaking layout of a box within a paragraph.
2117 }
bb7bbd12 2118
603f702b
JS
2119 // TODO: (also in para layout) should set the
2120 // object's size to an absolute one if specified,
2121 // but if not specified, calculate it from content.
2122
2123 // We need to add back the margins etc.
2124 {
2125 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2126 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
8db2e3ef 2127 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2128 SetCachedSize(marginRect.GetSize());
2129 }
2130
2131 // The maximum size is the greatest of all maximum widths for all paragraphs.
2132 {
2133 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2134 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2135 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2136 SetMaxSize(marginRect.GetSize());
2137 }
2138
2139 // The minimum size is the greatest of all minimum widths for all paragraphs.
2140 {
2141 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2142 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2143 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2144 SetMinSize(marginRect.GetSize());
2145 }
2146
8db2e3ef
JS
2147 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2148 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
603f702b
JS
2149 {
2150 int yOffset = 0;
2151 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2152 if (leftOverSpace > 0)
2153 {
8db2e3ef 2154 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
603f702b
JS
2155 {
2156 yOffset = (leftOverSpace/2);
2157 }
8db2e3ef 2158 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
603f702b
JS
2159 {
2160 yOffset = leftOverSpace;
2161 }
2162 }
7fe8059f 2163
603f702b
JS
2164 // Move all the children to vertically align the content
2165 // This doesn't take into account floating objects, unfortunately.
2166 if (yOffset != 0)
2167 {
2168 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
2169 while (node)
2170 {
2171 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2172 if (child)
603f702b 2173 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
2174
2175 node = node->GetNext();
2c375f42 2176 }
2c375f42 2177 }
5d7836c4
JS
2178 }
2179
1e967276 2180 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2181
2182 return true;
2183}
2184
5d7836c4 2185/// Get/set the size for the given range.
914a4e23 2186bool 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
2187{
2188 wxSize sz;
2189
09f14108
JS
2190 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2191 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2192
2193 // First find the first paragraph whose starting position is within the range.
2194 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2195 while (node)
2196 {
2197 // child is a paragraph
2198 wxRichTextObject* child = node->GetData();
2199 const wxRichTextRange& r = child->GetRange();
2200
2201 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2202 {
2203 startPara = node;
2204 break;
2205 }
2206
2207 node = node->GetNext();
2208 }
2209
2210 // Next find the last paragraph containing part of the range
2211 node = m_children.GetFirst();
2212 while (node)
2213 {
2214 // child is a paragraph
2215 wxRichTextObject* child = node->GetData();
2216 const wxRichTextRange& r = child->GetRange();
2217
2218 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2219 {
2220 endPara = node;
2221 break;
2222 }
2223
2224 node = node->GetNext();
2225 }
2226
2227 if (!startPara || !endPara)
2228 return false;
2229
2230 // Now we can add up the sizes
2231 for (node = startPara; node ; node = node->GetNext())
2232 {
2233 // child is a paragraph
2234 wxRichTextObject* child = node->GetData();
2235 const wxRichTextRange& childRange = child->GetRange();
2236 wxRichTextRange rangeToFind = range;
2237 rangeToFind.LimitTo(childRange);
2238
603f702b
JS
2239 if (child->IsTopLevel())
2240 rangeToFind = child->GetOwnRange();
2241
5d7836c4
JS
2242 wxSize childSize;
2243
2244 int childDescent = 0;
914a4e23 2245 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize);
5d7836c4
JS
2246
2247 descent = wxMax(childDescent, descent);
2248
2249 sz.x = wxMax(sz.x, childSize.x);
2250 sz.y += childSize.y;
2251
2252 if (node == endPara)
2253 break;
2254 }
2255
2256 size = sz;
2257
2258 return true;
2259}
2260
2261/// Get the paragraph at the given position
2262wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2263{
2264 if (caretPosition)
2265 pos ++;
2266
2267 // First find the first paragraph whose starting position is within the range.
2268 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2269 while (node)
2270 {
2271 // child is a paragraph
2272 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2273 // wxASSERT (child != NULL);
5d7836c4 2274
603f702b
JS
2275 if (child)
2276 {
2277 // Return first child in buffer if position is -1
2278 // if (pos == -1)
2279 // return child;
5d7836c4 2280
603f702b
JS
2281 if (child->GetRange().Contains(pos))
2282 return child;
2283 }
5d7836c4
JS
2284
2285 node = node->GetNext();
2286 }
2287 return NULL;
2288}
2289
2290/// Get the line at the given position
2291wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2292{
2293 if (caretPosition)
2294 pos ++;
2295
2296 // First find the first paragraph whose starting position is within the range.
2297 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2298 while (node)
2299 {
7051fa41
JS
2300 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2301 if (obj->GetRange().Contains(pos))
5d7836c4 2302 {
7051fa41
JS
2303 // child is a paragraph
2304 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2305 // wxASSERT (child != NULL);
7051fa41 2306
603f702b 2307 if (child)
7051fa41 2308 {
603f702b
JS
2309 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2310 while (node2)
2311 {
2312 wxRichTextLine* line = node2->GetData();
5d7836c4 2313
603f702b 2314 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2315
603f702b 2316 if (range.Contains(pos) ||
5d7836c4 2317
603f702b
JS
2318 // If the position is end-of-paragraph, then return the last line of
2319 // of the paragraph.
2320 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2321 return line;
5d7836c4 2322
603f702b
JS
2323 node2 = node2->GetNext();
2324 }
7051fa41 2325 }
7fe8059f 2326 }
5d7836c4
JS
2327
2328 node = node->GetNext();
2329 }
2330
2331 int lineCount = GetLineCount();
2332 if (lineCount > 0)
2333 return GetLineForVisibleLineNumber(lineCount-1);
2334 else
2335 return NULL;
2336}
2337
2338/// Get the line at the given y pixel position, or the last line.
2339wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2340{
2341 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2342 while (node)
2343 {
2344 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2345 // wxASSERT (child != NULL);
5d7836c4 2346
603f702b 2347 if (child)
5d7836c4 2348 {
603f702b
JS
2349 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2350 while (node2)
2351 {
2352 wxRichTextLine* line = node2->GetData();
5d7836c4 2353
603f702b 2354 wxRect rect(line->GetRect());
5d7836c4 2355
603f702b
JS
2356 if (y <= rect.GetBottom())
2357 return line;
5d7836c4 2358
603f702b
JS
2359 node2 = node2->GetNext();
2360 }
7fe8059f 2361 }
5d7836c4
JS
2362
2363 node = node->GetNext();
2364 }
2365
2366 // Return last line
2367 int lineCount = GetLineCount();
2368 if (lineCount > 0)
2369 return GetLineForVisibleLineNumber(lineCount-1);
2370 else
2371 return NULL;
2372}
2373
2374/// Get the number of visible lines
2375int wxRichTextParagraphLayoutBox::GetLineCount() const
2376{
2377 int count = 0;
2378
2379 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2380 while (node)
2381 {
2382 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2383 // wxASSERT (child != NULL);
2384
2385 if (child)
2386 count += child->GetLines().GetCount();
5d7836c4 2387
5d7836c4
JS
2388 node = node->GetNext();
2389 }
2390 return count;
2391}
2392
2393
2394/// Get the paragraph for a given line
2395wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2396{
1e967276 2397 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2398}
2399
2400/// Get the line size at the given position
2401wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2402{
2403 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2404 if (line)
2405 {
2406 return line->GetSize();
2407 }
2408 else
2409 return wxSize(0, 0);
2410}
2411
2412
2413/// Convenience function to add a paragraph of text
24777478 2414wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2415{
fe5aa22c 2416 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2417 // be combined at display time.
2418 // Divide into paragraph and character styles.
3e541562 2419
24777478
JS
2420 wxRichTextAttr defaultCharStyle;
2421 wxRichTextAttr defaultParaStyle;
4f32b3cf 2422
5607c890
JS
2423 // If the default style is a named paragraph style, don't apply any character formatting
2424 // to the initial text string.
2425 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2426 {
2427 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2428 if (def)
2429 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2430 }
2431 else
2432 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2433
24777478
JS
2434 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2435 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2436
2437 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
32423dd8 2438 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4
JS
2439
2440 AppendChild(para);
2441
2442 UpdateRanges();
5d7836c4
JS
2443
2444 return para->GetRange();
2445}
2446
2447/// Adds multiple paragraphs, based on newlines.
24777478 2448wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2449{
fe5aa22c 2450 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2451 // be combined at display time.
2452 // Divide into paragraph and character styles.
3e541562 2453
24777478
JS
2454 wxRichTextAttr defaultCharStyle;
2455 wxRichTextAttr defaultParaStyle;
5607c890
JS
2456
2457 // If the default style is a named paragraph style, don't apply any character formatting
2458 // to the initial text string.
2459 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2460 {
2461 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2462 if (def)
2463 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2464 }
2465 else
2466 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2467
24777478
JS
2468 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2469 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2470
5d7836c4
JS
2471 wxRichTextParagraph* firstPara = NULL;
2472 wxRichTextParagraph* lastPara = NULL;
2473
2474 wxRichTextRange range(-1, -1);
0ca07313 2475
5d7836c4 2476 size_t i = 0;
28f92d74 2477 size_t len = text.length();
5d7836c4 2478 wxString line;
4f32b3cf 2479 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2480 para->GetAttributes().GetTextBoxAttr().Reset();
0ca07313
JS
2481
2482 AppendChild(para);
2483
2484 firstPara = para;
2485 lastPara = para;
2486
5d7836c4
JS
2487 while (i < len)
2488 {
2489 wxChar ch = text[i];
2490 if (ch == wxT('\n') || ch == wxT('\r'))
2491 {
99404ab0
JS
2492 if (i != (len-1))
2493 {
2494 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2495 plainText->SetText(line);
0ca07313 2496
99404ab0 2497 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2498 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4 2499
99404ab0 2500 AppendChild(para);
0ca07313 2501
99404ab0
JS
2502 lastPara = para;
2503 line = wxEmptyString;
2504 }
5d7836c4
JS
2505 }
2506 else
2507 line += ch;
2508
2509 i ++;
2510 }
0ca07313 2511
7fe8059f 2512 if (!line.empty())
5d7836c4 2513 {
0ca07313
JS
2514 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2515 plainText->SetText(line);
5d7836c4
JS
2516 }
2517
5d7836c4 2518 UpdateRanges();
0ca07313 2519
0ca07313 2520 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2521}
2522
2523/// Convenience function to add an image
24777478 2524wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2525{
fe5aa22c 2526 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2527 // be combined at display time.
2528 // Divide into paragraph and character styles.
3e541562 2529
24777478
JS
2530 wxRichTextAttr defaultCharStyle;
2531 wxRichTextAttr defaultParaStyle;
5607c890
JS
2532
2533 // If the default style is a named paragraph style, don't apply any character formatting
2534 // to the initial text string.
2535 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2536 {
2537 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2538 if (def)
2539 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2540 }
2541 else
2542 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2543
24777478
JS
2544 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2545 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2546
4f32b3cf 2547 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
32423dd8 2548 para->GetAttributes().GetTextBoxAttr().Reset();
4f32b3cf
JS
2549 AppendChild(para);
2550 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2551
5d7836c4 2552 UpdateRanges();
5d7836c4
JS
2553
2554 return para->GetRange();
2555}
2556
2557
2558/// Insert fragment into this box at the given position. If partialParagraph is true,
2559/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2560/// marker.
5d7836c4 2561
0ca07313 2562bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2563{
5d7836c4
JS
2564 // First, find the first paragraph whose starting position is within the range.
2565 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2566 if (para)
2567 {
24777478 2568 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2569
5d7836c4
JS
2570 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2571
2572 // Now split at this position, returning the object to insert the new
2573 // ones in front of.
2574 wxRichTextObject* nextObject = para->SplitAt(position);
2575
2576 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2577 // text, for example, so let's optimize.
2578
2579 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2580 {
2581 // Add the first para to this para...
2582 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2583 if (!firstParaNode)
2584 return false;
2585
2586 // Iterate through the fragment paragraph inserting the content into this paragraph.
2587 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2588 wxASSERT (firstPara != NULL);
2589
2590 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2591 while (objectNode)
2592 {
2593 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2594
5d7836c4
JS
2595 if (!nextObject)
2596 {
2597 // Append
2598 para->AppendChild(newObj);
2599 }
2600 else
2601 {
2602 // Insert before nextObject
2603 para->InsertChild(newObj, nextObject);
2604 }
7fe8059f 2605
5d7836c4
JS
2606 objectNode = objectNode->GetNext();
2607 }
2608
2609 return true;
2610 }
2611 else
2612 {
2613 // Procedure for inserting a fragment consisting of a number of
2614 // paragraphs:
2615 //
2616 // 1. Remove and save the content that's after the insertion point, for adding
2617 // back once we've added the fragment.
2618 // 2. Add the content from the first fragment paragraph to the current
2619 // paragraph.
2620 // 3. Add remaining fragment paragraphs after the current paragraph.
2621 // 4. Add back the saved content from the first paragraph. If partialParagraph
2622 // is true, add it to the last paragraph added and not a new one.
2623
2624 // 1. Remove and save objects after split point.
2625 wxList savedObjects;
2626 if (nextObject)
2627 para->MoveToList(nextObject, savedObjects);
2628
2629 // 2. Add the content from the 1st fragment paragraph.
2630 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2631 if (!firstParaNode)
2632 return false;
2633
2634 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2635 wxASSERT(firstPara != NULL);
2636
6c0ea513
JS
2637 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2638 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2639
2640 // Save empty paragraph attributes for appending later
2641 // These are character attributes deliberately set for a new paragraph. Without this,
2642 // we couldn't pass default attributes when appending a new paragraph.
24777478 2643 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2644
5d7836c4 2645 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2646
2647 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2648 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2649
5d7836c4
JS
2650 while (objectNode)
2651 {
c025e094 2652 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2653
c025e094
JS
2654 // Append
2655 para->AppendChild(newObj);
7fe8059f 2656
5d7836c4
JS
2657 objectNode = objectNode->GetNext();
2658 }
2659
2660 // 3. Add remaining fragment paragraphs after the current paragraph.
2661 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2662 wxRichTextObject* nextParagraph = NULL;
2663 if (nextParagraphNode)
2664 nextParagraph = nextParagraphNode->GetData();
2665
2666 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2667 wxRichTextParagraph* finalPara = para;
2668
99404ab0
JS
2669 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2670
5d7836c4 2671 // If there was only one paragraph, we need to insert a new one.
99404ab0 2672 while (i)
5d7836c4 2673 {
99404ab0
JS
2674 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2675 wxASSERT( para != NULL );
5d7836c4 2676
99404ab0 2677 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2678
2679 if (nextParagraph)
2680 InsertChild(finalPara, nextParagraph);
2681 else
7fe8059f 2682 AppendChild(finalPara);
99404ab0
JS
2683
2684 i = i->GetNext();
5d7836c4 2685 }
5d7836c4 2686
99404ab0
JS
2687 // If there was only one paragraph, or we have full paragraphs in our fragment,
2688 // we need to insert a new one.
2689 if (needExtraPara)
2690 {
2691 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2692
2693 if (nextParagraph)
2694 InsertChild(finalPara, nextParagraph);
2695 else
2696 AppendChild(finalPara);
5d7836c4
JS
2697 }
2698
2699 // 4. Add back the remaining content.
2700 if (finalPara)
2701 {
c025e094
JS
2702 if (nextObject)
2703 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2704
2705 // Ensure there's at least one object
2706 if (finalPara->GetChildCount() == 0)
2707 {
7fe8059f 2708 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2709 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2710
2711 finalPara->AppendChild(text);
2712 }
2713 }
2714
6c0ea513
JS
2715 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2716 finalPara->SetAttributes(firstPara->GetAttributes());
2717 else if (finalPara && finalPara != para)
99404ab0
JS
2718 finalPara->SetAttributes(originalAttr);
2719
5d7836c4
JS
2720 return true;
2721 }
2722 }
2723 else
2724 {
2725 // Append
2726 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2727 while (i)
2728 {
2729 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2730 wxASSERT( para != NULL );
7fe8059f 2731
5d7836c4 2732 AppendChild(para->Clone());
7fe8059f 2733
5d7836c4
JS
2734 i = i->GetNext();
2735 }
2736
2737 return true;
2738 }
5d7836c4
JS
2739}
2740
2741/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2742/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2743bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2744{
2745 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2746 while (i)
2747 {
2748 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2749 wxASSERT( para != NULL );
2750
2751 if (!para->GetRange().IsOutside(range))
2752 {
2753 fragment.AppendChild(para->Clone());
7fe8059f 2754 }
5d7836c4
JS
2755 i = i->GetNext();
2756 }
2757
2758 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2759 if (!fragment.IsEmpty())
2760 {
5d7836c4
JS
2761 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2762 wxASSERT( firstPara != NULL );
2763
0e190fa2
JS
2764 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2765 wxASSERT( lastPara != NULL );
2766
2767 if (!firstPara || !lastPara)
2768 return false;
2769
2770 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2771
2772 long firstPos = firstPara->GetRange().GetStart();
2773
2774 // Adjust for renumbering from zero
2775 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2776
2777 long end;
2778 fragment.CalculateRange(0, end);
2779
5d7836c4 2780 // Chop off the start of the paragraph
0e190fa2 2781 if (topTailRange.GetStart() > 0)
5d7836c4 2782 {
0e190fa2 2783 wxRichTextRange r(0, topTailRange.GetStart()-1);
5d7836c4
JS
2784 firstPara->DeleteRange(r);
2785
2786 // Make sure the numbering is correct
0e190fa2 2787 fragment.CalculateRange(0, end);
5d7836c4
JS
2788
2789 // Now, we've deleted some positions, so adjust the range
2790 // accordingly.
0e190fa2
JS
2791 topTailRange.SetStart(range.GetLength());
2792 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2793 }
2794 else
2795 {
2796 topTailRange.SetStart(range.GetLength());
2797 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
5d7836c4
JS
2798 }
2799
61e6149e 2800 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
5d7836c4 2801 {
0e190fa2 2802 lastPara->DeleteRange(topTailRange);
5d7836c4
JS
2803
2804 // Make sure the numbering is correct
2805 long end;
0e190fa2 2806 fragment.CalculateRange(0, end);
5d7836c4
JS
2807
2808 // We only have part of a paragraph at the end
2809 fragment.SetPartialParagraph(true);
2810 }
2811 else
2812 {
0e190fa2
JS
2813 // We have a partial paragraph (don't save last new paragraph marker)
2814 // or complete paragraph
2815 fragment.SetPartialParagraph(isFragment);
5d7836c4
JS
2816 }
2817 }
2818
2819 return true;
2820}
2821
2822/// Given a position, get the number of the visible line (potentially many to a paragraph),
2823/// starting from zero at the start of the buffer.
2824long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2825{
2826 if (caretPosition)
2827 pos ++;
2828
2829 int lineCount = 0;
2830
2831 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2832 while (node)
2833 {
2834 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2835 // wxASSERT( child != NULL );
5d7836c4 2836
603f702b 2837 if (child)
5d7836c4 2838 {
603f702b 2839 if (child->GetRange().Contains(pos))
5d7836c4 2840 {
603f702b
JS
2841 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2842 while (node2)
5d7836c4 2843 {
603f702b
JS
2844 wxRichTextLine* line = node2->GetData();
2845 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2846
603f702b
JS
2847 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2848 {
2849 // If the caret is displayed at the end of the previous wrapped line,
2850 // we want to return the line it's _displayed_ at (not the actual line
2851 // containing the position).
2852 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2853 return lineCount - 1;
2854 else
2855 return lineCount;
2856 }
7fe8059f 2857
603f702b
JS
2858 lineCount ++;
2859
2860 node2 = node2->GetNext();
2861 }
2862 // If we didn't find it in the lines, it must be
2863 // the last position of the paragraph. So return the last line.
2864 return lineCount-1;
5d7836c4 2865 }
603f702b
JS
2866 else
2867 lineCount += child->GetLines().GetCount();
5d7836c4 2868 }
5d7836c4
JS
2869
2870 node = node->GetNext();
2871 }
2872
2873 // Not found
2874 return -1;
2875}
2876
2877/// Given a line number, get the corresponding wxRichTextLine object.
2878wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2879{
2880 int lineCount = 0;
2881
2882 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2883 while (node)
2884 {
2885 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2886 // wxASSERT(child != NULL);
5d7836c4 2887
603f702b 2888 if (child)
5d7836c4 2889 {
603f702b 2890 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2891 {
603f702b
JS
2892 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2893 while (node2)
2894 {
2895 wxRichTextLine* line = node2->GetData();
7fe8059f 2896
603f702b
JS
2897 if (lineCount == lineNumber)
2898 return line;
5d7836c4 2899
603f702b 2900 lineCount ++;
7fe8059f 2901
603f702b
JS
2902 node2 = node2->GetNext();
2903 }
7fe8059f 2904 }
603f702b
JS
2905 else
2906 lineCount += child->GetLines().GetCount();
5d7836c4 2907 }
5d7836c4
JS
2908
2909 node = node->GetNext();
2910 }
2911
2912 // Didn't find it
2913 return NULL;
2914}
2915
2916/// Delete range from layout.
2917bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2918{
2919 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2920
99404ab0 2921 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2922 while (node)
2923 {
2924 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2925 // wxASSERT (obj != NULL);
5d7836c4
JS
2926
2927 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2928
603f702b 2929 if (obj)
5d7836c4 2930 {
603f702b 2931 // Delete the range in each paragraph
99404ab0 2932
603f702b 2933 if (!obj->GetRange().IsOutside(range))
5d7836c4 2934 {
603f702b
JS
2935 // Deletes the content of this object within the given range
2936 obj->DeleteRange(range);
99404ab0 2937
603f702b
JS
2938 wxRichTextRange thisRange = obj->GetRange();
2939 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2940
603f702b
JS
2941 // If the whole paragraph is within the range to delete,
2942 // delete the whole thing.
2943 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2944 {
603f702b
JS
2945 // Delete the whole object
2946 RemoveChild(obj, true);
2947 obj = NULL;
99404ab0 2948 }
603f702b
JS
2949 else if (!firstPara)
2950 firstPara = obj;
5d7836c4 2951
603f702b
JS
2952 // If the range includes the paragraph end, we need to join this
2953 // and the next paragraph.
2954 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2955 {
603f702b
JS
2956 // We need to move the objects from the next paragraph
2957 // to this paragraph
2958
2959 wxRichTextParagraph* nextParagraph = NULL;
2960 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2961 nextParagraph = obj;
6c0ea513 2962 else
603f702b
JS
2963 {
2964 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2965 if (next)
2966 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2967 }
5d7836c4 2968
603f702b
JS
2969 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2970
2971 wxRichTextAttr nextParaAttr;
2972 if (applyFinalParagraphStyle)
2973 {
2974 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2975 // not the next one.
2976 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2977 nextParaAttr = thisAttr;
2978 else
2979 nextParaAttr = nextParagraph->GetAttributes();
2980 }
5d7836c4 2981
603f702b 2982 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2983 {
603f702b
JS
2984 // Move the objects to the previous para
2985 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2986
603f702b
JS
2987 while (node1)
2988 {
2989 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2990
603f702b 2991 firstPara->AppendChild(obj1);
5d7836c4 2992
603f702b
JS
2993 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2994 nextParagraph->GetChildren().Erase(node1);
99404ab0 2995
603f702b
JS
2996 node1 = next1;
2997 }
5d7836c4 2998
603f702b
JS
2999 // Delete the paragraph
3000 RemoveChild(nextParagraph, true);
3001 }
fa01bfdd 3002
603f702b
JS
3003 // Avoid empty paragraphs
3004 if (firstPara && firstPara->GetChildren().GetCount() == 0)
3005 {
3006 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
3007 firstPara->AppendChild(text);
3008 }
99404ab0 3009
603f702b
JS
3010 if (applyFinalParagraphStyle)
3011 firstPara->SetAttributes(nextParaAttr);
3012
3013 return true;
3014 }
5d7836c4
JS
3015 }
3016 }
7fe8059f 3017
5d7836c4
JS
3018 node = next;
3019 }
7fe8059f 3020
5d7836c4
JS
3021 return true;
3022}
3023
3024/// Get any text in this object for the given range
3025wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
3026{
3027 int lineCount = 0;
3028 wxString text;
3029 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3030 while (node)
3031 {
3032 wxRichTextObject* child = node->GetData();
3033 if (!child->GetRange().IsOutside(range))
3034 {
5d7836c4
JS
3035 wxRichTextRange childRange = range;
3036 childRange.LimitTo(child->GetRange());
7fe8059f 3037
5d7836c4 3038 wxString childText = child->GetTextForRange(childRange);
7fe8059f 3039
5d7836c4
JS
3040 text += childText;
3041
1a75935d 3042 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
3043 text += wxT("\n");
3044
5d7836c4
JS
3045 lineCount ++;
3046 }
3047 node = node->GetNext();
3048 }
3049
3050 return text;
3051}
3052
3053/// Get all the text
3054wxString wxRichTextParagraphLayoutBox::GetText() const
3055{
c99f1b0f 3056 return GetTextForRange(GetOwnRange());
5d7836c4
JS
3057}
3058
3059/// Get the paragraph by number
3060wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
3061{
27e20452 3062 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
3063 return NULL;
3064
3065 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
3066}
3067
3068/// Get the length of the paragraph
3069int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3070{
3071 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3072 if (para)
3073 return para->GetRange().GetLength() - 1; // don't include newline
3074 else
3075 return 0;
3076}
3077
3078/// Get the text of the paragraph
3079wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3080{
3081 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3082 if (para)
3083 return para->GetTextForRange(para->GetRange());
3084 else
3085 return wxEmptyString;
3086}
3087
3088/// Convert zero-based line column and paragraph number to a position.
3089long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3090{
3091 wxRichTextParagraph* para = GetParagraphAtLine(y);
3092 if (para)
3093 {
3094 return para->GetRange().GetStart() + x;
3095 }
3096 else
3097 return -1;
3098}
3099
3100/// Convert zero-based position to line column and paragraph number
3101bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3102{
3103 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3104 if (para)
3105 {
3106 int count = 0;
3107 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3108 while (node)
3109 {
3110 wxRichTextObject* child = node->GetData();
3111 if (child == para)
3112 break;
3113 count ++;
3114 node = node->GetNext();
3115 }
3116
3117 *y = count;
3118 *x = pos - para->GetRange().GetStart();
3119
3120 return true;
3121 }
3122 else
3123 return false;
3124}
3125
3126/// Get the leaf object in a paragraph at this position.
3127/// Given a line number, get the corresponding wxRichTextLine object.
3128wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3129{
3130 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3131 if (para)
3132 {
3133 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 3134
5d7836c4
JS
3135 while (node)
3136 {
3137 wxRichTextObject* child = node->GetData();
3138 if (child->GetRange().Contains(position))
3139 return child;
7fe8059f 3140
5d7836c4
JS
3141 node = node->GetNext();
3142 }
3143 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3144 return para->GetChildren().GetLast()->GetData();
3145 }
3146 return NULL;
3147}
3148
3149/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 3150bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
3151{
3152 bool characterStyle = false;
3153 bool paragraphStyle = false;
3154
3155 if (style.IsCharacterStyle())
3156 characterStyle = true;
3157 if (style.IsParagraphStyle())
3158 paragraphStyle = true;
3159
603f702b
JS
3160 wxRichTextBuffer* buffer = GetBuffer();
3161
59509217
JS
3162 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3163 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3164 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3165 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 3166 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 3167 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
3168
3169 // Apply paragraph style first, if any
24777478 3170 wxRichTextAttr wholeStyle(style);
523d2f14 3171
603f702b 3172 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 3173 {
603f702b 3174 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 3175 if (def)
603f702b 3176 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 3177 }
59509217
JS
3178
3179 // Limit the attributes to be set to the content to only character attributes.
24777478 3180 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
3181 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3182
603f702b 3183 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 3184 {
603f702b 3185 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 3186 if (def)
603f702b 3187 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
3188 }
3189
5d7836c4
JS
3190 // If we are associated with a control, make undoable; otherwise, apply immediately
3191 // to the data.
3192
603f702b 3193 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3194
3195 wxRichTextAction* action = NULL;
7fe8059f 3196
5d7836c4
JS
3197 if (haveControl && withUndo)
3198 {
603f702b 3199 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3200 action->SetRange(range);
603f702b 3201 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3202 }
3203
3204 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3205 while (node)
3206 {
3207 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3208 // wxASSERT (para != NULL);
5d7836c4
JS
3209
3210 if (para && para->GetChildCount() > 0)
3211 {
3212 // Stop searching if we're beyond the range of interest
3213 if (para->GetRange().GetStart() > range.GetEnd())
3214 break;
3215
3216 if (!para->GetRange().IsOutside(range))
3217 {
3218 // We'll be using a copy of the paragraph to make style changes,
3219 // not updating the buffer directly.
4e09ebe8 3220 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3221
5d7836c4
JS
3222 if (haveControl && withUndo)
3223 {
3224 newPara = new wxRichTextParagraph(*para);
3225 action->GetNewParagraphs().AppendChild(newPara);
3226
3227 // Also store the old ones for Undo
3228 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3229 }
3230 else
3231 newPara = para;
41a85215 3232
a7ed48a5
JS
3233 // If we're specifying paragraphs only, then we really mean character formatting
3234 // to be included in the paragraph style
3235 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3236 {
aeb6ebe2
JS
3237 if (removeStyle)
3238 {
3239 // Removes the given style from the paragraph
3240 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3241 }
3242 else if (resetExistingStyle)
523d2f14
JS
3243 newPara->GetAttributes() = wholeStyle;
3244 else
59509217 3245 {
523d2f14
JS
3246 if (applyMinimal)
3247 {
3248 // Only apply attributes that will make a difference to the combined
3249 // style as seen on the display
603f702b 3250 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3251 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3252 }
3253 else
3254 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3255 }
59509217 3256 }
5d7836c4 3257
5912d19e 3258 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3259 // since they will computed as needed. Only apply the character styling if it's _only_
3260 // character styling. This policy is subject to change and might be put under user control.
3261
59509217
JS
3262 // Hm. we might well be applying a mix of paragraph and character styles, in which
3263 // case we _do_ want to apply character styles regardless of what para styles are set.
3264 // But if we're applying a paragraph style, which has some character attributes, but
3265 // we only want the paragraphs to hold this character style, then we _don't_ want to
3266 // apply the character style. So we need to be able to choose.
3267
f1d800d9 3268 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3269 {
3270 wxRichTextRange childRange(range);
3271 childRange.LimitTo(newPara->GetRange());
7fe8059f 3272
5d7836c4
JS
3273 // Find the starting position and if necessary split it so
3274 // we can start applying a different style.
3275 // TODO: check that the style actually changes or is different
3276 // from style outside of range
4e09ebe8
JS
3277 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3278 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3279
5d7836c4
JS
3280 if (childRange.GetStart() == newPara->GetRange().GetStart())
3281 firstObject = newPara->GetChildren().GetFirst()->GetData();
3282 else
3283 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3284
5d7836c4
JS
3285 // Increment by 1 because we're apply the style one _after_ the split point
3286 long splitPoint = childRange.GetEnd();
3287 if (splitPoint != newPara->GetRange().GetEnd())
3288 splitPoint ++;
7fe8059f 3289
5d7836c4 3290 // Find last object
4b3483e7 3291 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3292 lastObject = newPara->GetChildren().GetLast()->GetData();
3293 else
3294 // lastObject is set as a side-effect of splitting. It's
3295 // returned as the object before the new object.
3296 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3297
5d7836c4
JS
3298 wxASSERT(firstObject != NULL);
3299 wxASSERT(lastObject != NULL);
7fe8059f 3300
5d7836c4
JS
3301 if (!firstObject || !lastObject)
3302 continue;
7fe8059f 3303
5d7836c4
JS
3304 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3305 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3306
4c9847e1
MW
3307 wxASSERT(firstNode);
3308 wxASSERT(lastNode);
7fe8059f 3309
5d7836c4 3310 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3311
5d7836c4
JS
3312 while (node2)
3313 {
3314 wxRichTextObject* child = node2->GetData();
7fe8059f 3315
aeb6ebe2
JS
3316 if (removeStyle)
3317 {
3318 // Removes the given style from the paragraph
3319 wxRichTextRemoveStyle(child->GetAttributes(), style);
3320 }
3321 else if (resetExistingStyle)
523d2f14
JS
3322 child->GetAttributes() = characterAttributes;
3323 else
59509217 3324 {
523d2f14
JS
3325 if (applyMinimal)
3326 {
3327 // Only apply attributes that will make a difference to the combined
3328 // style as seen on the display
603f702b 3329 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3330 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3331 }
3332 else
3333 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3334 }
59509217 3335
5d7836c4
JS
3336 if (node2 == lastNode)
3337 break;
7fe8059f 3338
5d7836c4
JS
3339 node2 = node2->GetNext();
3340 }
3341 }
3342 }
3343 }
3344
3345 node = node->GetNext();
3346 }
3347
3348 // Do action, or delay it until end of batch.
3349 if (haveControl && withUndo)
603f702b 3350 buffer->SubmitAction(action);
5d7836c4
JS
3351
3352 return true;
3353}
3354
603f702b
JS
3355// Just change the attributes for this single object.
3356void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3357{
603f702b 3358 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3359 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3360 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3361 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3362
cdaed652 3363 wxRichTextAction *action = NULL;
603f702b
JS
3364 wxRichTextAttr newAttr = obj->GetAttributes();
3365 if (resetExistingStyle)
3366 newAttr = textAttr;
3367 else
3368 newAttr.Apply(textAttr);
cdaed652
VZ
3369
3370 if (haveControl && withUndo)
3371 {
603f702b
JS
3372 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3373 action->SetRange(obj->GetRange().FromInternal());
3374 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3375 action->MakeObject(obj);
bec80f4f 3376
603f702b 3377 action->GetAttributes() = newAttr;
cdaed652
VZ
3378 }
3379 else
603f702b 3380 obj->GetAttributes() = newAttr;
cdaed652
VZ
3381
3382 if (haveControl && withUndo)
603f702b 3383 buffer->SubmitAction(action);
cdaed652
VZ
3384}
3385
5d7836c4 3386/// Get the text attributes for this position.
24777478 3387bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3388{
fe5aa22c
JS
3389 return DoGetStyle(position, style, true);
3390}
e191ee87 3391
24777478 3392bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3393{
3394 return DoGetStyle(position, style, false);
3395}
3396
fe5aa22c
JS
3397/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3398/// context attributes.
24777478 3399bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3400{
4e09ebe8 3401 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3402
5d7836c4 3403 if (style.IsParagraphStyle())
fe5aa22c 3404 {
5d7836c4 3405 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3406 if (obj)
3407 {
fe5aa22c
JS
3408 if (combineStyles)
3409 {
3410 // Start with the base style
3411 style = GetAttributes();
32423dd8 3412 style.GetTextBoxAttr().Reset();
e191ee87 3413
fe5aa22c
JS
3414 // Apply the paragraph style
3415 wxRichTextApplyStyle(style, obj->GetAttributes());
3416 }
3417 else
3418 style = obj->GetAttributes();
5912d19e 3419
fe5aa22c
JS
3420 return true;
3421 }
5d7836c4
JS
3422 }
3423 else
fe5aa22c
JS
3424 {
3425 obj = GetLeafObjectAtPosition(position);
3426 if (obj)
3427 {
fe5aa22c
JS
3428 if (combineStyles)
3429 {
3430 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3431 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3432 }
3433 else
3434 style = obj->GetAttributes();
5912d19e 3435
fe5aa22c
JS
3436 return true;
3437 }
fe5aa22c
JS
3438 }
3439 return false;
5d7836c4
JS
3440}
3441
59509217
JS
3442static bool wxHasStyle(long flags, long style)
3443{
3444 return (flags & style) != 0;
3445}
3446
3447/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3448/// content.
24777478
JS
3449bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3450{
3451 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3452
3453 return true;
3454}
3455
3456/// Get the combined style for a range - if any attribute is different within the range,
3457/// that attribute is not present within the flags.
3458/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3459/// nested.
3460bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3461{
24777478
JS
3462 style = wxRichTextAttr();
3463
c4168888 3464 wxRichTextAttr clashingAttrPara, clashingAttrChar;
24777478 3465 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3466
24777478
JS
3467 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3468 while (node)
59509217 3469 {
603f702b
JS
3470 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3471 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3472 {
24777478 3473 if (para->GetChildren().GetCount() == 0)
59509217 3474 {
603f702b 3475 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3476
c4168888 3477 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
59509217
JS
3478 }
3479 else
3480 {
24777478
JS
3481 wxRichTextRange paraRange(para->GetRange());
3482 paraRange.LimitTo(range);
59509217 3483
24777478
JS
3484 // First collect paragraph attributes only
3485 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3486 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
c4168888 3487 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
9c4cb611 3488
24777478
JS
3489 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3490
3491 while (childNode)
59509217 3492 {
24777478
JS
3493 wxRichTextObject* child = childNode->GetData();
3494 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3495 {
603f702b 3496 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3497
24777478
JS
3498 // Now collect character attributes only
3499 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3500
c4168888 3501 CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
24777478 3502 }
59509217 3503
24777478 3504 childNode = childNode->GetNext();
59509217
JS
3505 }
3506 }
59509217 3507 }
24777478 3508 node = node->GetNext();
59509217 3509 }
24777478
JS
3510 return true;
3511}
59509217 3512
24777478
JS
3513/// Set default style
3514bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3515{
3516 m_defaultAttributes = style;
3517 return true;
3518}
59509217 3519
24777478
JS
3520/// Test if this whole range has character attributes of the specified kind. If any
3521/// of the attributes are different within the range, the test fails. You
3522/// can use this to implement, for example, bold button updating. style must have
3523/// flags indicating which attributes are of interest.
3524bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3525{
3526 int foundCount = 0;
3527 int matchingCount = 0;
59509217 3528
24777478
JS
3529 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3530 while (node)
59509217 3531 {
24777478 3532 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3533 // wxASSERT (para != NULL);
59509217 3534
24777478 3535 if (para)
59509217 3536 {
24777478
JS
3537 // Stop searching if we're beyond the range of interest
3538 if (para->GetRange().GetStart() > range.GetEnd())
3539 return foundCount == matchingCount && foundCount != 0;
59509217 3540
24777478 3541 if (!para->GetRange().IsOutside(range))
59509217 3542 {
24777478 3543 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3544
24777478
JS
3545 while (node2)
3546 {
3547 wxRichTextObject* child = node2->GetData();
3548 // Allow for empty string if no buffer
3549 wxRichTextRange childRange = child->GetRange();
3550 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3551 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3552
345c78ca 3553 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
24777478
JS
3554 {
3555 foundCount ++;
3556 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3557
32423dd8 3558 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
24777478
JS
3559 matchingCount ++;
3560 }
59509217 3561
24777478
JS
3562 node2 = node2->GetNext();
3563 }
59509217
JS
3564 }
3565 }
59509217 3566
24777478 3567 node = node->GetNext();
59509217
JS
3568 }
3569
24777478
JS
3570 return foundCount == matchingCount && foundCount != 0;
3571}
59509217 3572
24777478
JS
3573/// Test if this whole range has paragraph attributes of the specified kind. If any
3574/// of the attributes are different within the range, the test fails. You
3575/// can use this to implement, for example, centering button updating. style must have
3576/// flags indicating which attributes are of interest.
3577bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3578{
3579 int foundCount = 0;
3580 int matchingCount = 0;
3581
3582 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3583 while (node)
38f833b1 3584 {
24777478 3585 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3586 // wxASSERT (para != NULL);
24777478
JS
3587
3588 if (para)
38f833b1 3589 {
24777478
JS
3590 // Stop searching if we're beyond the range of interest
3591 if (para->GetRange().GetStart() > range.GetEnd())
3592 return foundCount == matchingCount && foundCount != 0;
3593
3594 if (!para->GetRange().IsOutside(range))
38f833b1 3595 {
24777478
JS
3596 wxRichTextAttr textAttr = GetAttributes();
3597 // Apply the paragraph style
3598 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3599
3600 foundCount ++;
32423dd8 3601 if (textAttr.EqPartial(style, false /* strong test */))
24777478 3602 matchingCount ++;
38f833b1
JS
3603 }
3604 }
24777478
JS
3605
3606 node = node->GetNext();
38f833b1 3607 }
24777478
JS
3608 return foundCount == matchingCount && foundCount != 0;
3609}
5d7836c4 3610
cc2aecde
JS
3611void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3612{
3613 wxRichTextBuffer* buffer = GetBuffer();
3614 if (buffer && buffer->GetRichTextCtrl())
3615 buffer->GetRichTextCtrl()->PrepareContent(container);
3616}
3617
590a0f8b
JS
3618/// Set character or paragraph properties
3619bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3620{
3621 wxRichTextBuffer* buffer = GetBuffer();
3622
3623 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3624 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3625 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3626 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3627 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3628
3629 // If we are associated with a control, make undoable; otherwise, apply immediately
3630 // to the data.
3631
3632 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3633
3634 wxRichTextAction* action = NULL;
3635
3636 if (haveControl && withUndo)
3637 {
3638 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3639 action->SetRange(range);
3640 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3641 }
3642
3643 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3644 while (node)
3645 {
3646 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3647 // wxASSERT (para != NULL);
3648
3649 if (para && para->GetChildCount() > 0)
3650 {
3651 // Stop searching if we're beyond the range of interest
3652 if (para->GetRange().GetStart() > range.GetEnd())
3653 break;
3654
3655 if (!para->GetRange().IsOutside(range))
3656 {
3657 // We'll be using a copy of the paragraph to make style changes,
3658 // not updating the buffer directly.
3659 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3660
3661 if (haveControl && withUndo)
3662 {
3663 newPara = new wxRichTextParagraph(*para);
3664 action->GetNewParagraphs().AppendChild(newPara);
3665
3666 // Also store the old ones for Undo
3667 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3668 }
3669 else
3670 newPara = para;
3671
3672 if (parasOnly)
3673 {
3674 if (removeProperties)
3675 {
3676 // Removes the given style from the paragraph
3677 // TODO
3678 newPara->GetProperties().RemoveProperties(properties);
3679 }
3680 else if (resetExistingProperties)
3681 newPara->GetProperties() = properties;
3682 else
3683 newPara->GetProperties().MergeProperties(properties);
3684 }
3685
3686 // When applying paragraph styles dynamically, don't change the text objects' attributes
3687 // since they will computed as needed. Only apply the character styling if it's _only_
3688 // character styling. This policy is subject to change and might be put under user control.
3689
3690 // Hm. we might well be applying a mix of paragraph and character styles, in which
3691 // case we _do_ want to apply character styles regardless of what para styles are set.
3692 // But if we're applying a paragraph style, which has some character attributes, but
3693 // we only want the paragraphs to hold this character style, then we _don't_ want to
3694 // apply the character style. So we need to be able to choose.
3695
3696 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3697 {
3698 wxRichTextRange childRange(range);
3699 childRange.LimitTo(newPara->GetRange());
3700
3701 // Find the starting position and if necessary split it so
3702 // we can start applying different properties.
3703 // TODO: check that the properties actually change or are different
3704 // from properties outside of range
3705 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3706 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3707
3708 if (childRange.GetStart() == newPara->GetRange().GetStart())
3709 firstObject = newPara->GetChildren().GetFirst()->GetData();
3710 else
3711 firstObject = newPara->SplitAt(range.GetStart());
3712
3713 // Increment by 1 because we're apply the style one _after_ the split point
3714 long splitPoint = childRange.GetEnd();
3715 if (splitPoint != newPara->GetRange().GetEnd())
3716 splitPoint ++;
3717
3718 // Find last object
3719 if (splitPoint == newPara->GetRange().GetEnd())
3720 lastObject = newPara->GetChildren().GetLast()->GetData();
3721 else
3722 // lastObject is set as a side-effect of splitting. It's
3723 // returned as the object before the new object.
3724 (void) newPara->SplitAt(splitPoint, & lastObject);
3725
3726 wxASSERT(firstObject != NULL);
3727 wxASSERT(lastObject != NULL);
3728
3729 if (!firstObject || !lastObject)
3730 continue;
3731
3732 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3733 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3734
3735 wxASSERT(firstNode);
3736 wxASSERT(lastNode);
3737
3738 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3739
3740 while (node2)
3741 {
3742 wxRichTextObject* child = node2->GetData();
3743
3744 if (removeProperties)
3745 {
3746 // Removes the given properties from the paragraph
3747 child->GetProperties().RemoveProperties(properties);
3748 }
3749 else if (resetExistingProperties)
3750 child->GetProperties() = properties;
3751 else
3752 {
3753 child->GetProperties().MergeProperties(properties);
3754 }
3755
3756 if (node2 == lastNode)
3757 break;
3758
3759 node2 = node2->GetNext();
3760 }
3761 }
3762 }
3763 }
3764
3765 node = node->GetNext();
3766 }
3767
3768 // Do action, or delay it until end of batch.
3769 if (haveControl && withUndo)
3770 buffer->SubmitAction(action);
3771
3772 return true;
3773}
3774
5d7836c4
JS
3775void wxRichTextParagraphLayoutBox::Reset()
3776{
3777 Clear();
3778
603f702b
JS
3779 wxRichTextBuffer* buffer = GetBuffer();
3780 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3781 {
603f702b
JS
3782 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3783 event.SetEventObject(buffer->GetRichTextCtrl());
3784 event.SetContainer(this);
cd8ba0d9
JS
3785
3786 buffer->SendEvent(event, true);
3787 }
3788
7fe8059f 3789 AddParagraph(wxEmptyString);
3e541562 3790
cc2aecde
JS
3791 PrepareContent(*this);
3792
603f702b 3793 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3794}
3795
38113684
JS
3796/// Invalidate the buffer. With no argument, invalidates whole buffer.
3797void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3798{
603f702b 3799 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3800
603f702b
JS
3801 DoInvalidate(invalidRange);
3802}
3803
3804// Do the (in)validation for this object only
3805void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3806{
1e967276 3807 if (invalidRange == wxRICHTEXT_ALL)
38113684 3808 {
1e967276 3809 m_invalidRange = wxRICHTEXT_ALL;
38113684 3810 }
1e967276 3811 // Already invalidating everything
603f702b
JS
3812 else if (m_invalidRange == wxRICHTEXT_ALL)
3813 {
3814 }
3815 else
3816 {
3817 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3818 m_invalidRange.SetStart(invalidRange.GetStart());
3819 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3820 m_invalidRange.SetEnd(invalidRange.GetEnd());
3821 }
3822}
39a1c2f2 3823
603f702b
JS
3824// Do the (in)validation both up and down the hierarchy
3825void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3826{
3827 Invalidate(invalidRange);
3828
3829 if (invalidRange != wxRICHTEXT_NONE)
3830 {
3831 // Now go up the hierarchy
3832 wxRichTextObject* thisObj = this;
3833 wxRichTextObject* p = GetParent();
3834 while (p)
3835 {
3836 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3837 if (l)
3838 l->DoInvalidate(thisObj->GetRange());
3839
3840 thisObj = p;
3841 p = p->GetParent();
3842 }
3843 }
38113684
JS
3844}
3845
3846/// Get invalid range, rounding to entire paragraphs if argument is true.
3847wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3848{
1e967276 3849 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3850 return m_invalidRange;
39a1c2f2 3851
38113684 3852 wxRichTextRange range = m_invalidRange;
39a1c2f2 3853
38113684
JS
3854 if (wholeParagraphs)
3855 {
3856 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3857 if (para1)
3858 range.SetStart(para1->GetRange().GetStart());
f7667b84
JS
3859
3860 // FIXME: be more intelligent about this. Check if we have floating objects
3861 // before the end of the range. But it's not clear how we can in general
3862 // tell where it's safe to stop laying out.
3863 // Anyway, this code is central to efficiency when laying in floating mode.
3864 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3865 {
3866 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
3867 if (para2)
3868 range.SetEnd(para2->GetRange().GetEnd());
3869 }
3870 else
3871 // Floating layout means that all children should be laid out,
3872 // because we can't tell how the whole buffer will be affected.
3873 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3874 }
3875 return range;
3876}
3877
fe5aa22c
JS
3878/// Apply the style sheet to the buffer, for example if the styles have changed.
3879bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3880{
3881 wxASSERT(styleSheet != NULL);
3882 if (!styleSheet)
3883 return false;
3884
3885 int foundCount = 0;
3886
44580804
JS
3887 wxRichTextAttr attr(GetBasicStyle());
3888 if (GetBasicStyle().HasParagraphStyleName())
3889 {
3890 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3891 if (paraDef)
3892 {
3893 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3894 SetBasicStyle(attr);
3895 foundCount ++;
3896 }
3897 }
3898
3899 if (GetBasicStyle().HasCharacterStyleName())
3900 {
3901 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3902 if (charDef)
3903 {
3904 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3905 SetBasicStyle(attr);
3906 foundCount ++;
3907 }
3908 }
3909
fe5aa22c
JS
3910 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3911 while (node)
3912 {
3913 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3914 // wxASSERT (para != NULL);
fe5aa22c
JS
3915
3916 if (para)
3917 {
38f833b1
JS
3918 // Combine paragraph and list styles. If there is a list style in the original attributes,
3919 // the current indentation overrides anything else and is used to find the item indentation.
3920 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3921 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3922 // exception as above).
3923 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3924 // So when changing a list style interactively, could retrieve level based on current style, then
3925 // set appropriate indent and apply new style.
41a85215 3926
bbd55ff9
JS
3927 int outline = -1;
3928 int num = -1;
3929 if (para->GetAttributes().HasOutlineLevel())
3930 outline = para->GetAttributes().GetOutlineLevel();
3931 if (para->GetAttributes().HasBulletNumber())
3932 num = para->GetAttributes().GetBulletNumber();
3933
38f833b1
JS
3934 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3935 {
3936 int currentIndent = para->GetAttributes().GetLeftIndent();
3937
3938 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3939 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3940 if (paraDef && !listDef)
3941 {
336d8ae9 3942 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3943 foundCount ++;
3944 }
3945 else if (listDef && !paraDef)
3946 {
3947 // Set overall style defined for the list style definition
336d8ae9 3948 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3949
3950 // Apply the style for this level
3951 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3952 foundCount ++;
3953 }
3954 else if (listDef && paraDef)
3955 {
3956 // Combines overall list style, style for level, and paragraph style
336d8ae9 3957 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3958 foundCount ++;
3959 }
3960 }
3961 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3962 {
3963 int currentIndent = para->GetAttributes().GetLeftIndent();
3964
3965 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3966
41a85215 3967 // Overall list definition style
336d8ae9 3968 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3969
38f833b1
JS
3970 // Style for this level
3971 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3972
3973 foundCount ++;
3974 }
3975 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3976 {
3977 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3978 if (def)
3979 {
336d8ae9 3980 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3981 foundCount ++;
3982 }
3983 }
bbd55ff9
JS
3984
3985 if (outline != -1)
3986 para->GetAttributes().SetOutlineLevel(outline);
3987 if (num != -1)
3988 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3989 }
3990
3991 node = node->GetNext();
3992 }
3993 return foundCount != 0;
3994}
3995
38f833b1
JS
3996/// Set list style
3997bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3998{
603f702b
JS
3999 wxRichTextBuffer* buffer = GetBuffer();
4000 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 4001
38f833b1
JS
4002 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4003 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4004 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4005 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4006
38f833b1
JS
4007 // Current number, if numbering
4008 int n = startFrom;
41a85215 4009
38f833b1
JS
4010 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4011
4012 // If we are associated with a control, make undoable; otherwise, apply immediately
4013 // to the data.
4014
603f702b 4015 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4016
4017 wxRichTextAction* action = NULL;
4018
4019 if (haveControl && withUndo)
4020 {
603f702b 4021 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4022 action->SetRange(range);
603f702b 4023 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4024 }
4025
4026 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4027 while (node)
4028 {
4029 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4030 // wxASSERT (para != NULL);
38f833b1
JS
4031
4032 if (para && para->GetChildCount() > 0)
4033 {
4034 // Stop searching if we're beyond the range of interest
4035 if (para->GetRange().GetStart() > range.GetEnd())
4036 break;
4037
4038 if (!para->GetRange().IsOutside(range))
4039 {
4040 // We'll be using a copy of the paragraph to make style changes,
4041 // not updating the buffer directly.
4042 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4043
4044 if (haveControl && withUndo)
4045 {
4046 newPara = new wxRichTextParagraph(*para);
4047 action->GetNewParagraphs().AppendChild(newPara);
4048
4049 // Also store the old ones for Undo
4050 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4051 }
4052 else
4053 newPara = para;
41a85215 4054
38f833b1
JS
4055 if (def)
4056 {
4057 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4058 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 4059
38f833b1
JS
4060 // How is numbering going to work?
4061 // If we are renumbering, or numbering for the first time, we need to keep
4062 // track of the number for each level. But we might be simply applying a different
4063 // list style.
4064 // In Word, applying a style to several paragraphs, even if at different levels,
4065 // reverts the level back to the same one. So we could do the same here.
4066 // Renumbering will need to be done when we promote/demote a paragraph.
4067
4068 // Apply the overall list style, and item style for this level
24777478 4069 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4070 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4071
d2d0adc7 4072 // Now we need to do numbering
4ce3ebd3
JS
4073 // Preserve the existing list item continuation bullet style, if any
4074 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4075 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4076 else
38f833b1 4077 {
4ce3ebd3
JS
4078 if (renumber)
4079 {
4080 newPara->GetAttributes().SetBulletNumber(n);
4081 }
41a85215 4082
4ce3ebd3
JS
4083 n ++;
4084 }
38f833b1
JS
4085 }
4086 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4087 {
4088 // if def is NULL, remove list style, applying any associated paragraph style
4089 // to restore the attributes
4090
4091 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4092 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 4093 newPara->GetAttributes().SetBulletText(wxEmptyString);
c4168888 4094 newPara->GetAttributes().SetBulletStyle(0);
41a85215 4095
38f833b1 4096 // Eliminate the main list-related attributes
d2d0adc7 4097 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 4098
38f833b1
JS
4099 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4100 {
4101 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4102 if (def)
4103 {
336d8ae9 4104 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
4105 }
4106 }
4107 }
4108 }
4109 }
4110
4111 node = node->GetNext();
4112 }
4113
4114 // Do action, or delay it until end of batch.
4115 if (haveControl && withUndo)
603f702b 4116 buffer->SubmitAction(action);
38f833b1
JS
4117
4118 return true;
4119}
4120
4121bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4122{
603f702b
JS
4123 wxRichTextBuffer* buffer = GetBuffer();
4124 if (buffer && buffer->GetStyleSheet())
38f833b1 4125 {
603f702b 4126 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4127 if (def)
4128 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4129 }
4130 return false;
4131}
4132
4133/// Clear list for given range
4134bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4135{
4136 return SetListStyle(range, NULL, flags);
4137}
4138
4139/// Number/renumber any list elements in the given range
4140bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4141{
4142 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4143}
4144
4145/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4146bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4147 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4148{
603f702b
JS
4149 wxRichTextBuffer* buffer = GetBuffer();
4150 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4151
38f833b1
JS
4152 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4153 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 4154#if wxDEBUG_LEVEL
38f833b1 4155 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 4156#endif
38f833b1
JS
4157
4158 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4159
38f833b1
JS
4160 // Max number of levels
4161 const int maxLevels = 10;
41a85215 4162
38f833b1
JS
4163 // The level we're looking at now
4164 int currentLevel = -1;
41a85215 4165
38f833b1
JS
4166 // The item number for each level
4167 int levels[maxLevels];
4168 int i;
41a85215 4169
38f833b1
JS
4170 // Reset all numbering
4171 for (i = 0; i < maxLevels; i++)
4172 {
4173 if (startFrom != -1)
d2d0adc7 4174 levels[i] = startFrom-1;
38f833b1 4175 else if (renumber) // start again
d2d0adc7 4176 levels[i] = 0;
38f833b1
JS
4177 else
4178 levels[i] = -1; // start from the number we found, if any
4179 }
41a85215 4180
bb7bbd12 4181#if wxDEBUG_LEVEL
38f833b1 4182 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
bb7bbd12 4183#endif
38f833b1
JS
4184
4185 // If we are associated with a control, make undoable; otherwise, apply immediately
4186 // to the data.
4187
603f702b 4188 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4189
4190 wxRichTextAction* action = NULL;
4191
4192 if (haveControl && withUndo)
4193 {
603f702b 4194 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4195 action->SetRange(range);
603f702b 4196 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4197 }
4198
4199 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4200 while (node)
4201 {
4202 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4203 // wxASSERT (para != NULL);
38f833b1
JS
4204
4205 if (para && para->GetChildCount() > 0)
4206 {
4207 // Stop searching if we're beyond the range of interest
4208 if (para->GetRange().GetStart() > range.GetEnd())
4209 break;
4210
4211 if (!para->GetRange().IsOutside(range))
4212 {
4213 // We'll be using a copy of the paragraph to make style changes,
4214 // not updating the buffer directly.
4215 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4216
4217 if (haveControl && withUndo)
4218 {
4219 newPara = new wxRichTextParagraph(*para);
4220 action->GetNewParagraphs().AppendChild(newPara);
4221
4222 // Also store the old ones for Undo
4223 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4224 }
4225 else
4226 newPara = para;
41a85215 4227
38f833b1
JS
4228 wxRichTextListStyleDefinition* defToUse = def;
4229 if (!defToUse)
4230 {
336d8ae9
VZ
4231 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4232 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 4233 }
41a85215 4234
38f833b1
JS
4235 if (defToUse)
4236 {
4237 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4238 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4239
d2d0adc7
JS
4240 // If we've specified a level to apply to all, change the level.
4241 if (specifiedLevel != -1)
38f833b1 4242 thisLevel = specifiedLevel;
41a85215 4243
38f833b1
JS
4244 // Do promotion if specified
4245 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4246 {
4247 thisLevel = thisLevel - promoteBy;
4248 if (thisLevel < 0)
4249 thisLevel = 0;
4250 if (thisLevel > 9)
4251 thisLevel = 9;
4252 }
41a85215 4253
38f833b1 4254 // Apply the overall list style, and item style for this level
24777478 4255 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4256 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4257
4ce3ebd3
JS
4258 // Preserve the existing list item continuation bullet style, if any
4259 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4260 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4261
38f833b1 4262 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 4263
38f833b1
JS
4264 if (currentLevel == -1)
4265 currentLevel = thisLevel;
41a85215 4266
38f833b1
JS
4267 // Same level as before, do nothing except increment level's number afterwards
4268 if (currentLevel == thisLevel)
4269 {
4270 }
4271 // A deeper level: start renumbering all levels after current level
4272 else if (thisLevel > currentLevel)
4273 {
4274 for (i = currentLevel+1; i <= thisLevel; i++)
4275 {
d2d0adc7 4276 levels[i] = 0;
38f833b1
JS
4277 }
4278 currentLevel = thisLevel;
4279 }
4280 else if (thisLevel < currentLevel)
4281 {
4282 currentLevel = thisLevel;
41a85215 4283 }
38f833b1
JS
4284
4285 // Use the current numbering if -1 and we have a bullet number already
4286 if (levels[currentLevel] == -1)
4287 {
4288 if (newPara->GetAttributes().HasBulletNumber())
4289 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4290 else
4291 levels[currentLevel] = 1;
4292 }
d2d0adc7
JS
4293 else
4294 {
4ce3ebd3
JS
4295 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4296 levels[currentLevel] ++;
d2d0adc7 4297 }
41a85215 4298
38f833b1
JS
4299 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4300
d2d0adc7
JS
4301 // Create the bullet text if an outline list
4302 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4303 {
4304 wxString text;
4305 for (i = 0; i <= currentLevel; i++)
4306 {
4307 if (!text.IsEmpty())
4308 text += wxT(".");
4309 text += wxString::Format(wxT("%d"), levels[i]);
4310 }
4311 newPara->GetAttributes().SetBulletText(text);
4312 }
38f833b1
JS
4313 }
4314 }
4315 }
4316
4317 node = node->GetNext();
4318 }
4319
4320 // Do action, or delay it until end of batch.
4321 if (haveControl && withUndo)
603f702b 4322 buffer->SubmitAction(action);
38f833b1
JS
4323
4324 return true;
4325}
4326
4327bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4328{
603f702b
JS
4329 wxRichTextBuffer* buffer = GetBuffer();
4330 if (buffer->GetStyleSheet())
38f833b1
JS
4331 {
4332 wxRichTextListStyleDefinition* def = NULL;
4333 if (!defName.IsEmpty())
603f702b 4334 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4335 return NumberList(range, def, flags, startFrom, specifiedLevel);
4336 }
4337 return false;
4338}
4339
4340/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4341bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4342{
4343 // TODO
4344 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4345 // to NumberList with a flag indicating promotion is required within one of the ranges.
4346 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4347 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4348 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4349 // list position will start from 1.
4350 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4351 // We can end the renumbering at this point.
41a85215 4352
38f833b1 4353 // For now, only renumber within the promotion range.
41a85215 4354
38f833b1
JS
4355 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4356}
4357
4358bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4359{
603f702b
JS
4360 wxRichTextBuffer* buffer = GetBuffer();
4361 if (buffer->GetStyleSheet())
38f833b1
JS
4362 {
4363 wxRichTextListStyleDefinition* def = NULL;
4364 if (!defName.IsEmpty())
603f702b 4365 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4366 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4367 }
4368 return false;
4369}
4370
d2d0adc7
JS
4371/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4372/// position of the paragraph that it had to start looking from.
24777478 4373bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 4374{
c4168888 4375 // TODO: add GetNextChild/GetPreviousChild to composite
4ce3ebd3
JS
4376 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4377 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4378 {
4379 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4380 if (node)
4381 {
4382 node = node->GetPrevious();
4383 if (node)
4384 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4385 else
4386 previousParagraph = NULL;
4387 }
4388 else
4389 previousParagraph = NULL;
4390 }
4391
c4168888 4392 if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
d2d0adc7 4393 return false;
3e541562 4394
603f702b
JS
4395 wxRichTextBuffer* buffer = GetBuffer();
4396 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4397 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 4398 {
336d8ae9 4399 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
4400 if (def)
4401 {
4402 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4403 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4404
d2d0adc7
JS
4405 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4406
4407 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4408 if (previousParagraph->GetAttributes().HasBulletName())
4409 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4410 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4411 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4412
d2d0adc7
JS
4413 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4414 attr.SetBulletNumber(nextNumber);
3e541562 4415
d2d0adc7
JS
4416 if (isOutline)
4417 {
4418 wxString text = previousParagraph->GetAttributes().GetBulletText();
4419 if (!text.IsEmpty())
4420 {
4421 int pos = text.Find(wxT('.'), true);
4422 if (pos != wxNOT_FOUND)
4423 {
4424 text = text.Mid(0, text.Length() - pos - 1);
4425 }
4426 else
4427 text = wxEmptyString;
4428 if (!text.IsEmpty())
4429 text += wxT(".");
4430 text += wxString::Format(wxT("%d"), nextNumber);
4431 attr.SetBulletText(text);
4432 }
4433 }
3e541562 4434
d2d0adc7
JS
4435 return true;
4436 }
4437 else
4438 return false;
4439 }
4440 else
4441 return false;
4442}
4443
5d7836c4
JS
4444/*!
4445 * wxRichTextParagraph
4446 * This object represents a single paragraph (or in a straight text editor, a line).
4447 */
4448
603f702b 4449IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4450
cfa3b256
JS
4451wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4452
24777478 4453wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4454 wxRichTextCompositeObject(parent)
5d7836c4 4455{
5d7836c4
JS
4456 if (style)
4457 SetAttributes(*style);
4458}
4459
24777478 4460wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4461 wxRichTextCompositeObject(parent)
5d7836c4 4462{
4f32b3cf
JS
4463 if (paraStyle)
4464 SetAttributes(*paraStyle);
5d7836c4 4465
4f32b3cf 4466 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4467}
4468
4469wxRichTextParagraph::~wxRichTextParagraph()
4470{
4471 ClearLines();
4472}
4473
4474/// Draw the item
8db2e3ef 4475bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4476{
603f702b
JS
4477 if (!IsShown())
4478 return true;
4479
4480 // Currently we don't merge these attributes with the parent, but we
4481 // should consider whether we should (e.g. if we set a border colour
4482 // for all paragraphs). But generally box attributes are likely to be
4483 // different for different objects.
4484 wxRect paraRect = GetRect();
24777478 4485 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef
JS
4486 context.ApplyVirtualAttributes(attr, this);
4487
4488 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
fe5aa22c 4489
5d7836c4 4490 // Draw the bullet, if any
4ce3ebd3 4491 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
5d7836c4 4492 {
fe5aa22c 4493 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4494 {
fe5aa22c 4495 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4496 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4497
8db2e3ef 4498 wxRichTextAttr bulletAttr(attr);
d2d0adc7 4499
e3eac0ff
JS
4500 // Combine with the font of the first piece of content, if one is specified
4501 if (GetChildren().GetCount() > 0)
4502 {
4503 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4504 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4505 {
4506 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4507 }
4508 }
4509
d2d0adc7 4510 // Get line height from first line, if any
d3b9f782 4511 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4512
4513 wxPoint linePos;
4514 int lineHeight wxDUMMY_INITIALIZE(0);
4515 if (line)
5d7836c4 4516 {
d2d0adc7
JS
4517 lineHeight = line->GetSize().y;
4518 linePos = line->GetPosition() + GetPosition();
5d7836c4 4519 }
d2d0adc7 4520 else
f089713f 4521 {
f089713f 4522 wxFont font;
44cc96a8
JS
4523 if (bulletAttr.HasFont() && GetBuffer())
4524 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4525 else
4526 font = (*wxNORMAL_FONT);
4527
ecb5fbf1 4528 wxCheckSetFont(dc, font);
f089713f 4529
d2d0adc7
JS
4530 lineHeight = dc.GetCharHeight();
4531 linePos = GetPosition();
4532 linePos.y += spaceBeforePara;
4533 }
f089713f 4534
d2d0adc7 4535 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4536
d2d0adc7
JS
4537 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4538 {
4539 if (wxRichTextBuffer::GetRenderer())
4540 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4541 }
3e541562
JS
4542 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4543 {
d2d0adc7
JS
4544 if (wxRichTextBuffer::GetRenderer())
4545 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4546 }
5d7836c4
JS
4547 else
4548 {
4549 wxString bulletText = GetBulletText();
3e541562 4550
d2d0adc7
JS
4551 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4552 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4553 }
4554 }
4555 }
7fe8059f 4556
5d7836c4
JS
4557 // Draw the range for each line, one object at a time.
4558
4559 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4560 while (node)
4561 {
4562 wxRichTextLine* line = node->GetData();
1e967276 4563 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4564
5d7836c4
JS
4565 // Lines are specified relative to the paragraph
4566
4567 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4568
7051fa41
JS
4569 // Don't draw if off the screen
4570 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4571 {
7051fa41
JS
4572 wxPoint objectPosition = linePosition;
4573 int maxDescent = line->GetDescent();
4574
4575 // Loop through objects until we get to the one within range
4576 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4577
7051fa41
JS
4578 int i = 0;
4579 while (node2)
5d7836c4 4580 {
7051fa41 4581 wxRichTextObject* child = node2->GetData();
5d7836c4 4582
e12b91a3 4583 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4584 {
7051fa41
JS
4585 // Draw this part of the line at the correct position
4586 wxRichTextRange objectRange(child->GetRange());
4587 objectRange.LimitTo(lineRange);
4588
4589 wxSize objectSize;
603f702b 4590 if (child->IsTopLevel())
7051fa41 4591 {
603f702b
JS
4592 objectSize = child->GetCachedSize();
4593 objectRange = child->GetOwnRange();
7051fa41
JS
4594 }
4595 else
7051fa41 4596 {
603f702b
JS
4597#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4598 if (i < (int) line->GetObjectSizes().GetCount())
4599 {
4600 objectSize.x = line->GetObjectSizes()[(size_t) i];
4601 }
4602 else
4603#endif
4604 {
4605 int descent = 0;
8db2e3ef 4606 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
603f702b 4607 }
7051fa41 4608 }
5d7836c4 4609
7051fa41
JS
4610 // Use the child object's width, but the whole line's height
4611 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
8db2e3ef 4612 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4613
7051fa41
JS
4614 objectPosition.x += objectSize.x;
4615 i ++;
4616 }
4617 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4618 // Can break out of inner loop now since we've passed this line's range
4619 break;
5d7836c4 4620
7051fa41
JS
4621 node2 = node2->GetNext();
4622 }
5d7836c4
JS
4623 }
4624
4625 node = node->GetNext();
7fe8059f 4626 }
5d7836c4
JS
4627
4628 return true;
4629}
4630
4f3d5bc0
JS
4631// Get the range width using partial extents calculated for the whole paragraph.
4632static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4633{
4634 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4635
affbfa1f
JS
4636 if (partialExtents.GetCount() < (size_t) range.GetLength())
4637 return 0;
4638
4f3d5bc0
JS
4639 int leftMostPos = 0;
4640 if (range.GetStart() - para.GetRange().GetStart() > 0)
4641 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4642
4643 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4644
4645 int w = rightMostPos - leftMostPos;
4646
4647 return w;
4648}
4649
5d7836c4 4650/// Lay the item out
8db2e3ef 4651bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 4652{
cdaed652
VZ
4653 // Deal with floating objects firstly before the normal layout
4654 wxRichTextBuffer* buffer = GetBuffer();
4655 wxASSERT(buffer);
e12b91a3 4656
07d4142f 4657 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
e12b91a3
JS
4658
4659 if (wxRichTextBuffer::GetFloatingLayoutMode())
4660 {
4661 wxASSERT(collector != NULL);
4662 if (collector)
4663 LayoutFloat(dc, context, rect, parentRect, style, collector);
4664 }
cdaed652 4665
24777478 4666 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef 4667 context.ApplyVirtualAttributes(attr, this);
fe5aa22c 4668
169adfa9
JS
4669 // ClearLines();
4670
5d7836c4 4671 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4672 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4673 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4674 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4675 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4676 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4677
4678 int lineSpacing = 0;
4679
4680 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
a1b806b9 4681 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
5d7836c4 4682 {
8f0e4366
JS
4683 wxCheckSetFont(dc, attr.GetFont());
4684 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
5d7836c4
JS
4685 }
4686
5d7836c4
JS
4687 // Start position for each line relative to the paragraph
4688 int startPositionFirstLine = leftIndent;
4689 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4690
4691 // If we have a bullet in this paragraph, the start position for the first line's text
4692 // is actually leftIndent + leftSubIndent.
fe5aa22c 4693 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4694 startPositionFirstLine = startPositionSubsequentLines;
4695
5d7836c4
JS
4696 long lastEndPos = GetRange().GetStart()-1;
4697 long lastCompletedEndPos = lastEndPos;
4698
4699 int currentWidth = 0;
4700 SetPosition(rect.GetPosition());
4701
4702 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4703 int lineHeight = 0;
4704 int maxWidth = 0;
603f702b 4705 int maxHeight = currentPosition.y;
476a319a 4706 int maxAscent = 0;
5d7836c4 4707 int maxDescent = 0;
5d7836c4 4708 int lineCount = 0;
cdaed652
VZ
4709 int lineAscent = 0;
4710 int lineDescent = 0;
5d7836c4 4711
2f45f554
JS
4712 wxRichTextObjectList::compatibility_iterator node;
4713
4714#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4715 wxUnusedVar(style);
4716 wxArrayInt partialExtents;
4717
4718 wxSize paraSize;
8aab23a1 4719 int paraDescent = 0;
2f45f554
JS
4720
4721 // This calculates the partial text extents
914a4e23 4722 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents);
2f45f554
JS
4723#else
4724 node = m_children.GetFirst();
ecb5fbf1
JS
4725 while (node)
4726 {
4727 wxRichTextObject* child = node->GetData();
4728
603f702b 4729 //child->SetCachedSize(wxDefaultSize);
8db2e3ef 4730 child->Layout(dc, context, rect, style);
ecb5fbf1
JS
4731
4732 node = node->GetNext();
4733 }
31778480
JS
4734#endif
4735
5d7836c4
JS
4736 // Split up lines
4737
4738 // We may need to go back to a previous child, in which case create the new line,
4739 // find the child corresponding to the start position of the string, and
4740 // continue.
4741
603f702b
JS
4742 wxRect availableRect;
4743
ecb5fbf1 4744 node = m_children.GetFirst();
5d7836c4
JS
4745 while (node)
4746 {
4747 wxRichTextObject* child = node->GetData();
4748
cdaed652 4749 // If floating, ignore. We already laid out floats.
603f702b
JS
4750 // Also ignore if empty object, except if we haven't got any
4751 // size yet.
e12b91a3
JS
4752 if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4753 || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
603f702b 4754 )
affbfa1f
JS
4755 {
4756 node = node->GetNext();
4757 continue;
4758 }
4759
5d7836c4
JS
4760 // If this is e.g. a composite text box, it will need to be laid out itself.
4761 // But if just a text fragment or image, for example, this will
4762 // do nothing. NB: won't we need to set the position after layout?
4763 // since for example if position is dependent on vertical line size, we
4764 // can't tell the position until the size is determined. So possibly introduce
4765 // another layout phase.
4766
5d7836c4
JS
4767 // We may only be looking at part of a child, if we searched back for wrapping
4768 // and found a suitable point some way into the child. So get the size for the fragment
4769 // if necessary.
3e541562 4770
ff76711f
JS
4771 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4772 long lastPosToUse = child->GetRange().GetEnd();
4773 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4774
ff76711f
JS
4775 if (lineBreakInThisObject)
4776 lastPosToUse = nextBreakPos;
5d7836c4
JS
4777
4778 wxSize childSize;
4779 int childDescent = 0;
3e541562 4780
603f702b
JS
4781 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4782 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4783 rect.width - startOffset - rightIndent, rect.height);
4784
4785 if (child->IsTopLevel())
4786 {
4787 wxSize oldSize = child->GetCachedSize();
4788
4789 child->Invalidate(wxRICHTEXT_ALL);
4790 child->SetPosition(wxPoint(0, 0));
4791
4792 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4793 // lays out the object again using the minimum size
4794 // The position will be determined by its location in its line,
4795 // and not by the child's actual position.
8db2e3ef
JS
4796 child->LayoutToBestSize(dc, context, buffer,
4797 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4798
4799 if (oldSize != child->GetCachedSize())
4800 {
4801 partialExtents.Clear();
4802
4803 // Recalculate the partial text extents since the child object changed size
914a4e23 4804 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
603f702b
JS
4805 }
4806 }
4807
4808 // Problem: we need to layout composites here for which we need the available width,
4809 // but we can't get the available width without using the float collector which
4810 // needs to know the object height.
4811
ff76711f 4812 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4813 {
4814 childSize = child->GetCachedSize();
4815 childDescent = child->GetDescent();
4816 }
4817 else
4f3d5bc0
JS
4818 {
4819#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4820 // Get height only, then the width using the partial extents
914a4e23 4821 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4f3d5bc0
JS
4822 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4823#else
914a4e23 4824 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize());
4f3d5bc0
JS
4825#endif
4826 }
ff76711f 4827
603f702b
JS
4828 bool doLoop = true;
4829 int loopIterations = 0;
4830
4831 // If there are nested objects that need to lay themselves out, we have to do this in a
4832 // loop because the height of the object may well depend on the available width.
4833 // And because of floating object positioning, the available width depends on the
4834 // height of the object and whether it will clash with the floating objects.
4835 // So, we see whether the available width changes due to the presence of floating images.
4836 // If it does, then we'll use the new restricted width to find the object height again.
4837 // If this causes another restriction in the available width, we'll try again, until
4838 // either we lose patience or the available width settles down.
4839 do
4840 {
4841 loopIterations ++;
4842
4843 wxRect oldAvailableRect = availableRect;
4844
4845 // Available width depends on the floating objects and the line height.
7c9fdebe 4846 // Note: the floating objects may be placed vertically along the two sides of
603f702b
JS
4847 // buffer, so we may have different available line widths with different
4848 // [startY, endY]. So, we can't determine how wide the available
4849 // space is until we know the exact line height.
a70eb13e
JS
4850 if (childDescent == 0)
4851 {
4852 lineHeight = wxMax(lineHeight, childSize.y);
4853 lineDescent = maxDescent;
4854 lineAscent = maxAscent;
4855 }
4856 else
4857 {
4858 lineDescent = wxMax(childDescent, maxDescent);
4859 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4860 }
4861 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
603f702b 4862
e12b91a3 4863 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
603f702b 4864 {
e12b91a3
JS
4865 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4866
4867 // Adjust availableRect to the space that is available when taking floating objects into account.
4868
4869 if (floatAvailableRect.x + startOffset > availableRect.x)
4870 {
4871 int newX = floatAvailableRect.x + startOffset;
4872 int newW = availableRect.width - (newX - availableRect.x);
4873 availableRect.x = newX;
4874 availableRect.width = newW;
4875 }
603f702b 4876
e12b91a3
JS
4877 if (floatAvailableRect.width < availableRect.width)
4878 availableRect.width = floatAvailableRect.width;
4879 }
603f702b
JS
4880
4881 currentPosition.x = availableRect.x - rect.x;
4882
4883 if (child->IsTopLevel() && loopIterations <= 20)
4884 {
4885 if (availableRect != oldAvailableRect)
4886 {
4887 wxSize oldSize = child->GetCachedSize();
4888
603f702b
JS
4889 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4890 // lays out the object again using the minimum size
4891 child->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 4892 child->LayoutToBestSize(dc, context, buffer,
914a4e23 4893 attr, child->GetAttributes(), availableRect, parentRect.GetSize(), style);
603f702b
JS
4894 childSize = child->GetCachedSize();
4895 childDescent = child->GetDescent();
603f702b
JS
4896
4897 if (oldSize != child->GetCachedSize())
4898 {
4899 partialExtents.Clear();
4900
4901 // Recalculate the partial text extents since the child object changed size
914a4e23 4902 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
603f702b 4903 }
cdaed652 4904
603f702b
JS
4905 // Go around the loop finding the available rect for the given floating objects
4906 }
4907 else
4908 doLoop = false;
4909 }
4910 else
4911 doLoop = false;
4912 }
4913 while (doLoop);
cdaed652 4914
20d09da5
JS
4915 if (child->IsTopLevel())
4916 {
4917 // We can move it to the correct position at this point
32423dd8
JS
4918 // TODO: probably need to add margin
4919 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
20d09da5
JS
4920 }
4921
ff76711f
JS
4922 // Cases:
4923 // 1) There was a line break BEFORE the natural break
4924 // 2) There was a line break AFTER the natural break
603f702b
JS
4925 // 3) It's the last line
4926 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4927
603f702b
JS
4928 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4929 ||
4930 (childSize.x + currentWidth > availableRect.width)
914a4e23 4931#if 0
603f702b
JS
4932 ||
4933 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
914a4e23 4934#endif
603f702b 4935 )
5d7836c4
JS
4936 {
4937 long wrapPosition = 0;
603f702b
JS
4938 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4939 wrapPosition = child->GetRange().GetEnd();
4940 else
5d7836c4
JS
4941
4942 // Find a place to wrap. This may walk back to previous children,
4943 // for example if a word spans several objects.
cdaed652
VZ
4944 // Note: one object must contains only one wxTextAtrr, so the line height will not
4945 // change inside one object. Thus, we can pass the remain line width to the
4946 // FindWrapPosition function.
8db2e3ef 4947 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4948 {
4949 // If the function failed, just cut it off at the end of this child.
4950 wrapPosition = child->GetRange().GetEnd();
4951 }
4952
4953 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4954 if (wrapPosition <= lastCompletedEndPos)
4955 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4956
603f702b
JS
4957 // Line end position shouldn't be the same as the end, or greater.
4958 if (wrapPosition >= GetRange().GetEnd())
a8a15de6 4959 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
603f702b 4960
5d7836c4 4961 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4962
5d7836c4
JS
4963 // Let's find the actual size of the current line now
4964 wxSize actualSize;
4965 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4966
a70eb13e 4967 childDescent = 0;
4ab8a5e2 4968
4f3d5bc0 4969#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4970 if (!child->IsEmpty())
4971 {
4972 // Get height only, then the width using the partial extents
914a4e23 4973 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
603f702b
JS
4974 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4975 }
4976 else
4f3d5bc0 4977#endif
914a4e23 4978 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
4f3d5bc0 4979
5d7836c4 4980 currentWidth = actualSize.x;
a70eb13e
JS
4981
4982 // The descent for the whole line at this point, is the correct max descent
4983 maxDescent = childDescent;
4984 // Maximum ascent
4985 maxAscent = actualSize.y-childDescent;
4986
4987 // lineHeight is given by the height for the whole line, since it will
4988 // take into account ascend/descend.
4989 lineHeight = actualSize.y;
7fe8059f 4990
07d4142f 4991 if (lineHeight == 0 && buffer)
603f702b 4992 {
07d4142f 4993 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
4994 wxCheckSetFont(dc, font);
4995 lineHeight = dc.GetCharHeight();
4996 }
4997
4998 if (maxDescent == 0)
4999 {
5000 int w, h;
5001 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5002 }
5003
5d7836c4 5004 // Add a new line
1e967276 5005 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 5006
1e967276
JS
5007 // Set relative range so we won't have to change line ranges when paragraphs are moved
5008 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
5009 line->SetPosition(currentPosition);
5010 line->SetSize(wxSize(currentWidth, lineHeight));
5011 line->SetDescent(maxDescent);
5012
603f702b
JS
5013 maxHeight = currentPosition.y + lineHeight;
5014
5d7836c4
JS
5015 // Now move down a line. TODO: add margins, spacing
5016 currentPosition.y += lineHeight;
5017 currentPosition.y += lineSpacing;
5d7836c4 5018 maxDescent = 0;
476a319a 5019 maxAscent = 0;
603f702b
JS
5020 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5021 currentWidth = 0;
7fe8059f 5022
5d7836c4
JS
5023 lineCount ++;
5024
a70eb13e 5025 // TODO: account for zero-length objects
603f702b 5026 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 5027
5d7836c4
JS
5028 lastEndPos = wrapPosition;
5029 lastCompletedEndPos = lastEndPos;
5030
5031 lineHeight = 0;
5032
603f702b
JS
5033 if (wrapPosition < GetRange().GetEnd()-1)
5034 {
5035 // May need to set the node back to a previous one, due to searching back in wrapping
5036 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5037 if (childAfterWrapPosition)
5038 node = m_children.Find(childAfterWrapPosition);
5039 else
5040 node = node->GetNext();
5041 }
5d7836c4
JS
5042 else
5043 node = node->GetNext();
603f702b
JS
5044
5045 // Apply paragraph styles such as alignment to the wrapped line
5046 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
5047 }
5048 else
5049 {
5050 // We still fit, so don't add a line, and keep going
5051 currentWidth += childSize.x;
a70eb13e
JS
5052
5053 if (childDescent == 0)
5054 {
5055 // An object with a zero descend value wants to take up the whole
5056 // height regardless of baseline
5057 lineHeight = wxMax(lineHeight, childSize.y);
5058 }
5059 else
5060 {
5061 maxDescent = wxMax(childDescent, maxDescent);
5062 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5063 }
5064
5065 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5d7836c4 5066
603f702b 5067 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
5068 lastEndPos = child->GetRange().GetEnd();
5069
5070 node = node->GetNext();
5071 }
5072 }
5073
07d4142f 5074 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 5075
914a4e23
JS
5076 // Add the last line - it's the current pos -> last para pos
5077 // Substract -1 because the last position is always the end-paragraph position.
5078 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
5079 {
5080 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
5081
5082 wxRichTextLine* line = AllocateLine(lineCount);
5083
5084 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
5085
5086 // Set relative range so we won't have to change line ranges when paragraphs are moved
5087 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5088
5089 line->SetPosition(currentPosition);
5090
5091 if (lineHeight == 0 && buffer)
5092 {
5093 wxFont font(buffer->GetFontTable().FindFont(attr));
5094 wxCheckSetFont(dc, font);
5095 lineHeight = dc.GetCharHeight();
5096 }
5097
5098 if (maxDescent == 0)
5099 {
5100 int w, h;
5101 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5102 }
5103
5104 line->SetSize(wxSize(currentWidth, lineHeight));
5105 line->SetDescent(maxDescent);
5106 currentPosition.y += lineHeight;
5107 currentPosition.y += lineSpacing;
5108 lineCount ++;
5109 }
5110
1e967276
JS
5111 // Remove remaining unused line objects, if any
5112 ClearUnusedLines(lineCount);
5113
603f702b
JS
5114 // We need to add back the margins etc.
5115 {
5116 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5117 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5118 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5119 SetCachedSize(marginRect.GetSize());
5120 }
5121
5122 // The maximum size is the length of the paragraph stretched out into a line.
5123 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5124 // this size. TODO: take into account line breaks.
5125 {
5126 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 5127 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 5128 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5129 SetMaxSize(marginRect.GetSize());
5130 }
5131
5132 // Find the greatest minimum size. Currently we only look at non-text objects,
5133 // which isn't ideal but it would be slow to find the maximum word width to
5134 // use as the minimum.
5135 {
5136 int minWidth = 0;
5137 node = m_children.GetFirst();
5138 while (node)
5139 {
5140 wxRichTextObject* child = node->GetData();
5141
5142 // If floating, ignore. We already laid out floats.
5143 // Also ignore if empty object, except if we haven't got any
5144 // size yet.
e12b91a3 5145 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
603f702b
JS
5146 {
5147 if (child->GetCachedSize().x > minWidth)
5148 minWidth = child->GetMinSize().x;
5149 }
5150 node = node->GetNext();
5151 }
5d7836c4 5152
603f702b
JS
5153 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5154 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5155 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5156 SetMinSize(marginRect.GetSize());
5157 }
5d7836c4 5158
2f45f554
JS
5159#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5160#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5161 // Use the text extents to calculate the size of each fragment in each line
5162 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5163 while (lineNode)
5164 {
5165 wxRichTextLine* line = lineNode->GetData();
5166 wxRichTextRange lineRange = line->GetAbsoluteRange();
5167
5168 // Loop through objects until we get to the one within range
5169 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5170
5171 while (node2)
5172 {
5173 wxRichTextObject* child = node2->GetData();
5174
affbfa1f 5175 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
5176 {
5177 wxRichTextRange rangeToUse = lineRange;
5178 rangeToUse.LimitTo(child->GetRange());
5179
5180 // Find the size of the child from the text extents, and store in an array
5181 // for drawing later
5182 int left = 0;
5183 if (rangeToUse.GetStart() > GetRange().GetStart())
5184 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5185 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5186 int sz = right - left;
5187 line->GetObjectSizes().Add(sz);
5188 }
5189 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5190 // Can break out of inner loop now since we've passed this line's range
5191 break;
5192
5193 node2 = node2->GetNext();
5194 }
5195
5196 lineNode = lineNode->GetNext();
5197 }
5198#endif
5199#endif
5200
5d7836c4
JS
5201 return true;
5202}
5203
603f702b
JS
5204/// Apply paragraph styles, such as centering, to wrapped lines
5205/// TODO: take into account box attributes, possibly
5206void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5207{
5208 if (!attr.HasAlignment())
5209 return;
5210
5211 wxPoint pos = line->GetPosition();
32423dd8 5212 wxPoint originalPos = pos;
603f702b
JS
5213 wxSize size = line->GetSize();
5214
5215 // centering, right-justification
8db2e3ef 5216 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5217 {
5218 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5219 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5220 line->SetPosition(pos);
5221 }
8db2e3ef 5222 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5223 {
5224 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5225 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5226 line->SetPosition(pos);
5227 }
32423dd8
JS
5228
5229 if (pos != originalPos)
5230 {
5231 wxPoint inc = pos - originalPos;
5232
5233 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5234
5235 while (node)
5236 {
5237 wxRichTextObject* child = node->GetData();
5238 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5239 child->Move(child->GetPosition() + inc);
5240
5241 node = node->GetNext();
5242 }
5243 }
603f702b 5244}
5d7836c4
JS
5245
5246/// Insert text at the given position
5247bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5248{
5249 wxRichTextObject* childToUse = NULL;
09f14108 5250 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5251
5252 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5253 while (node)
5254 {
5255 wxRichTextObject* child = node->GetData();
5256 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5257 {
5258 childToUse = child;
5259 nodeToUse = node;
5260 break;
5261 }
5262
5263 node = node->GetNext();
5264 }
5265
5266 if (childToUse)
5267 {
5268 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5269 if (textObject)
5270 {
5271 int posInString = pos - textObject->GetRange().GetStart();
5272
5273 wxString newText = textObject->GetText().Mid(0, posInString) +
5274 text + textObject->GetText().Mid(posInString);
5275 textObject->SetText(newText);
5276
28f92d74 5277 int textLength = text.length();
5d7836c4
JS
5278
5279 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5280 textObject->GetRange().GetEnd() + textLength));
5281
5282 // Increment the end range of subsequent fragments in this paragraph.
5283 // We'll set the paragraph range itself at a higher level.
5284
5285 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5286 while (node)
5287 {
5288 wxRichTextObject* child = node->GetData();
5289 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5290 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5291
5d7836c4
JS
5292 node = node->GetNext();
5293 }
5294
5295 return true;
5296 }
5297 else
5298 {
5299 // TODO: if not a text object, insert at closest position, e.g. in front of it
5300 }
5301 }
5302 else
5303 {
5304 // Add at end.
5305 // Don't pass parent initially to suppress auto-setting of parent range.
5306 // We'll do that at a higher level.
5307 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5308
5309 AppendChild(textObject);
5310 return true;
5311 }
5312
5313 return false;
5314}
5315
5316void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5317{
bec80f4f 5318 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5319}
5320
5321/// Clear the cached lines
5322void wxRichTextParagraph::ClearLines()
5323{
5324 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5325}
5326
5327/// Get/set the object size for the given range. Returns false if the range
5328/// is invalid for this object.
914a4e23 5329bool 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
5330{
5331 if (!range.IsWithin(GetRange()))
5332 return false;
5333
5334 if (flags & wxRICHTEXT_UNFORMATTED)
5335 {
5336 // Just use unformatted data, assume no line breaks
5d7836c4
JS
5337 wxSize sz;
5338
31778480
JS
5339 wxArrayInt childExtents;
5340 wxArrayInt* p;
5341 if (partialExtents)
5342 p = & childExtents;
5343 else
5344 p = NULL;
5345
a70eb13e
JS
5346 int maxDescent = 0;
5347 int maxAscent = 0;
5348 int maxLineHeight = 0;
5349
5d7836c4
JS
5350 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5351 while (node)
5352 {
5353 wxRichTextObject* child = node->GetData();
5354 if (!child->GetRange().IsOutside(range))
5355 {
cdaed652 5356 // Floating objects have a zero size within the paragraph.
e12b91a3 5357 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
cdaed652
VZ
5358 {
5359 if (partialExtents)
5360 {
5361 int lastSize;
5362 if (partialExtents->GetCount() > 0)
5363 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5364 else
5365 lastSize = 0;
5366
5367 partialExtents->Add(0 /* zero size */ + lastSize);
5368 }
5369 }
5370 else
5371 {
603f702b 5372 wxSize childSize;
4f3d5bc0 5373
603f702b
JS
5374 wxRichTextRange rangeToUse = range;
5375 rangeToUse.LimitTo(child->GetRange());
603f702b 5376 int childDescent = 0;
31778480 5377
7c9fdebe 5378 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
603f702b
JS
5379 // but it's only going to be used after caching has taken place.
5380 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5381 {
603f702b
JS
5382 childDescent = child->GetDescent();
5383 childSize = child->GetCachedSize();
2f45f554 5384
a70eb13e
JS
5385 if (childDescent == 0)
5386 {
5387 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5388 }
5389 else
5390 {
5391 maxDescent = wxMax(maxDescent, childDescent);
5392 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5393 }
5394
5395 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5396
5397 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5398 sz.x += childSize.x;
a70eb13e 5399 descent = maxDescent;
603f702b
JS
5400 }
5401 else if (child->IsTopLevel())
31778480 5402 {
603f702b
JS
5403 childDescent = child->GetDescent();
5404 childSize = child->GetCachedSize();
31778480 5405
a70eb13e
JS
5406 if (childDescent == 0)
5407 {
5408 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5409 }
5410 else
5411 {
5412 maxDescent = wxMax(maxDescent, childDescent);
5413 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5414 }
5415
5416 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5417
5418 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5419 sz.x += childSize.x;
a70eb13e
JS
5420 descent = maxDescent;
5421
5422 // FIXME: this won't change the original values.
5423 // Should we be calling GetRangeSize above instead of using cached values?
5424#if 0
603f702b 5425 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5426 {
603f702b
JS
5427 child->SetCachedSize(childSize);
5428 child->SetDescent(childDescent);
31778480 5429 }
a70eb13e 5430#endif
31778480 5431
603f702b
JS
5432 if (partialExtents)
5433 {
5434 int lastSize;
5435 if (partialExtents->GetCount() > 0)
5436 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5437 else
5438 lastSize = 0;
5439
5440 partialExtents->Add(childSize.x + lastSize);
5441 }
5442 }
914a4e23 5443 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
603f702b 5444 {
a70eb13e
JS
5445 if (childDescent == 0)
5446 {
5447 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5448 }
5449 else
5450 {
5451 maxDescent = wxMax(maxDescent, childDescent);
5452 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5453 }
5454
5455 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5456
5457 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5458 sz.x += childSize.x;
a70eb13e 5459 descent = maxDescent;
603f702b
JS
5460
5461 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5462 {
5463 child->SetCachedSize(childSize);
5464 child->SetDescent(childDescent);
5465 }
5466
5467 if (partialExtents)
5468 {
5469 int lastSize;
5470 if (partialExtents->GetCount() > 0)
5471 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5472 else
5473 lastSize = 0;
5474
5475 size_t i;
5476 for (i = 0; i < childExtents.GetCount(); i++)
5477 {
5478 partialExtents->Add(childExtents[i] + lastSize);
5479 }
5480 }
5481 }
5482 }
5483
5484 if (p)
5485 p->Clear();
5d7836c4
JS
5486 }
5487
5488 node = node->GetNext();
5489 }
5490 size = sz;
5491 }
5492 else
5493 {
5494 // Use formatted data, with line breaks
5495 wxSize sz;
5496
5497 // We're going to loop through each line, and then for each line,
5498 // call GetRangeSize for the fragment that comprises that line.
5499 // Only we have to do that multiple times within the line, because
5500 // the line may be broken into pieces. For now ignore line break commands
5501 // (so we can assume that getting the unformatted size for a fragment
5502 // within a line is the actual size)
5503
5504 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5505 while (node)
5506 {
5507 wxRichTextLine* line = node->GetData();
1e967276
JS
5508 wxRichTextRange lineRange = line->GetAbsoluteRange();
5509 if (!lineRange.IsOutside(range))
5d7836c4 5510 {
a70eb13e
JS
5511 int maxDescent = 0;
5512 int maxAscent = 0;
5513 int maxLineHeight = 0;
5514 int maxLineWidth = 0;
7fe8059f 5515
5d7836c4
JS
5516 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5517 while (node2)
5518 {
5519 wxRichTextObject* child = node2->GetData();
7fe8059f 5520
e12b91a3 5521 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5d7836c4 5522 {
1e967276 5523 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5524 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5525 if (child->IsTopLevel())
5526 rangeToUse = child->GetOwnRange();
7fe8059f 5527
5d7836c4
JS
5528 wxSize childSize;
5529 int childDescent = 0;
914a4e23 5530 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
5d7836c4 5531 {
a70eb13e
JS
5532 if (childDescent == 0)
5533 {
5534 // Assume that if descent is zero, this child can occupy the full line height
5535 // and does not need space for the line's maximum descent. So we influence
5536 // the overall max line height only.
5537 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5538 }
5539 else
5540 {
5541 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5542 maxDescent = wxMax(maxAscent, childDescent);
5543 }
5544 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5545 maxLineWidth += childSize.x;
5d7836c4 5546 }
5d7836c4 5547 }
7fe8059f 5548
5d7836c4
JS
5549 node2 = node2->GetNext();
5550 }
5551
a70eb13e
JS
5552 descent = wxMax(descent, maxDescent);
5553
5d7836c4 5554 // Increase size by a line (TODO: paragraph spacing)
a70eb13e
JS
5555 sz.y += maxLineHeight;
5556 sz.x = wxMax(sz.x, maxLineWidth);
5d7836c4
JS
5557 }
5558 node = node->GetNext();
5559 }
5560 size = sz;
5561 }
5562 return true;
5563}
5564
5565/// Finds the absolute position and row height for the given character position
8db2e3ef 5566bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5567{
5568 if (index == -1)
5569 {
5570 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5571 if (line)
5572 *height = line->GetSize().y;
5573 else
5574 *height = dc.GetCharHeight();
5575
5576 // -1 means 'the start of the buffer'.
5577 pt = GetPosition();
5578 if (line)
5579 pt = pt + line->GetPosition();
5580
5d7836c4
JS
5581 return true;
5582 }
5583
5584 // The final position in a paragraph is taken to mean the position
5585 // at the start of the next paragraph.
5586 if (index == GetRange().GetEnd())
5587 {
5588 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5589 wxASSERT( parent != NULL );
5590
5591 // Find the height at the next paragraph, if any
5592 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5593 if (line)
5594 {
5595 *height = line->GetSize().y;
5596 pt = line->GetAbsolutePosition();
5597 }
5598 else
5599 {
5600 *height = dc.GetCharHeight();
5601 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5602 pt = wxPoint(indent, GetCachedSize().y);
5603 }
5604
5605 return true;
5606 }
5607
5608 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5609 return false;
5610
5611 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5612 while (node)
5613 {
5614 wxRichTextLine* line = node->GetData();
1e967276
JS
5615 wxRichTextRange lineRange = line->GetAbsoluteRange();
5616 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5617 {
5618 // If this is the last point in the line, and we're forcing the
5619 // returned value to be the start of the next line, do the required
5620 // thing.
1e967276 5621 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5622 {
5623 if (node->GetNext())
5624 {
5625 wxRichTextLine* nextLine = node->GetNext()->GetData();
5626 *height = nextLine->GetSize().y;
5627 pt = nextLine->GetAbsolutePosition();
5628 return true;
5629 }
5630 }
5631
5632 pt.y = line->GetPosition().y + GetPosition().y;
5633
1e967276 5634 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5635 wxSize rangeSize;
5636 int descent = 0;
5637
5638 // We find the size of the line up to this point,
5639 // then we can add this size to the line start position and
5640 // paragraph start position to find the actual position.
5641
8db2e3ef 5642 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5643 {
5644 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5645 *height = line->GetSize().y;
5646
5647 return true;
5648 }
5649
5650 }
5651
5652 node = node->GetNext();
5653 }
5654
5655 return false;
5656}
5657
5658/// Hit-testing: returns a flag indicating hit test details, plus
5659/// information about position
8db2e3ef 5660int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5661{
603f702b
JS
5662 if (!IsShown())
5663 return wxRICHTEXT_HITTEST_NONE;
5664
5665 // If we're in the top-level container, then we can return
5666 // a suitable hit test code even if the point is outside the container area,
5667 // so that we can position the caret sensibly even if we don't
5668 // click on valid content. If we're not at the top-level, and the point
5669 // is not within this paragraph object, then we don't want to stop more
5670 // precise hit-testing from working prematurely, so return immediately.
5671 // NEW STRATEGY: use the parent boundary to test whether we're in the
5672 // right region, not the paragraph, since the paragraph may be positioned
5673 // some way in from where the user clicks.
5674 {
5675 long tmpPos;
5676 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5677 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5678 return wxRICHTEXT_HITTEST_NONE;
5679 }
5680
5681 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5682 while (objNode)
5683 {
5684 wxRichTextObject* child = objNode->GetData();
7c9fdebe
JS
5685 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5686 // and also, if this seems composite but actually is marked as atomic,
5687 // don't recurse.
5688 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5689 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
603f702b
JS
5690 {
5691 {
8db2e3ef 5692 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5693 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5694 return hitTest;
5695 }
5696 }
5697
5698 objNode = objNode->GetNext();
5699 }
5700
5d7836c4
JS
5701 wxPoint paraPos = GetPosition();
5702
5703 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5704 while (node)
5705 {
5706 wxRichTextLine* line = node->GetData();
5707 wxPoint linePos = paraPos + line->GetPosition();
5708 wxSize lineSize = line->GetSize();
1e967276 5709 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5710
62381daa 5711 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5712 {
5713 if (pt.x < linePos.x)
5714 {
1e967276 5715 textPosition = lineRange.GetStart();
603f702b
JS
5716 *obj = FindObjectAtPosition(textPosition);
5717 *contextObj = GetContainer();
f262b25c 5718 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5719 }
5720 else if (pt.x >= (linePos.x + lineSize.x))
5721 {
1e967276 5722 textPosition = lineRange.GetEnd();
603f702b
JS
5723 *obj = FindObjectAtPosition(textPosition);
5724 *contextObj = GetContainer();
f262b25c 5725 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5726 }
5727 else
5728 {
2f45f554
JS
5729#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5730 wxArrayInt partialExtents;
5731
5732 wxSize paraSize;
5733 int paraDescent;
5734
5735 // This calculates the partial text extents
914a4e23 5736 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
2f45f554
JS
5737
5738 int lastX = linePos.x;
5739 size_t i;
5740 for (i = 0; i < partialExtents.GetCount(); i++)
5741 {
5742 int nextX = partialExtents[i] + linePos.x;
5743
5744 if (pt.x >= lastX && pt.x <= nextX)
5745 {
5746 textPosition = i + lineRange.GetStart(); // minus 1?
5747
603f702b
JS
5748 *obj = FindObjectAtPosition(textPosition);
5749 *contextObj = GetContainer();
5750
2f45f554
JS
5751 // So now we know it's between i-1 and i.
5752 // Let's see if we can be more precise about
5753 // which side of the position it's on.
5754
cdaed652 5755 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5756 if (pt.x >= midPoint)
5757 return wxRICHTEXT_HITTEST_AFTER;
5758 else
5759 return wxRICHTEXT_HITTEST_BEFORE;
5760 }
5761
5762 lastX = nextX;
5763 }
5764#else
5d7836c4
JS
5765 long i;
5766 int lastX = linePos.x;
1e967276 5767 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5768 {
5769 wxSize childSize;
5770 int descent = 0;
7fe8059f 5771
1e967276 5772 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5773
8db2e3ef 5774 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5775
5776 int nextX = childSize.x + linePos.x;
5777
5778 if (pt.x >= lastX && pt.x <= nextX)
5779 {
5780 textPosition = i;
5781
603f702b
JS
5782 *obj = FindObjectAtPosition(textPosition);
5783 *contextObj = GetContainer();
5784
5d7836c4
JS
5785 // So now we know it's between i-1 and i.
5786 // Let's see if we can be more precise about
5787 // which side of the position it's on.
5788
cdaed652 5789 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5790 if (pt.x >= midPoint)
5791 return wxRICHTEXT_HITTEST_AFTER;
5792 else
5793 return wxRICHTEXT_HITTEST_BEFORE;
5794 }
5795 else
5796 {
5797 lastX = nextX;
5798 }
5799 }
2f45f554 5800#endif
5d7836c4
JS
5801 }
5802 }
7fe8059f 5803
5d7836c4
JS
5804 node = node->GetNext();
5805 }
5806
5807 return wxRICHTEXT_HITTEST_NONE;
5808}
5809
5810/// Split an object at this position if necessary, and return
5811/// the previous object, or NULL if inserting at beginning.
5812wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5813{
5814 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5815 while (node)
5816 {
5817 wxRichTextObject* child = node->GetData();
5818
5819 if (pos == child->GetRange().GetStart())
5820 {
5821 if (previousObject)
4d551ad5
JS
5822 {
5823 if (node->GetPrevious())
5824 *previousObject = node->GetPrevious()->GetData();
5825 else
5826 *previousObject = NULL;
5827 }
5d7836c4
JS
5828
5829 return child;
5830 }
5831
5832 if (child->GetRange().Contains(pos))
5833 {
5834 // This should create a new object, transferring part of
5835 // the content to the old object and the rest to the new object.
5836 wxRichTextObject* newObject = child->DoSplit(pos);
5837
5838 // If we couldn't split this object, just insert in front of it.
5839 if (!newObject)
5840 {
5841 // Maybe this is an empty string, try the next one
5842 // return child;
5843 }
5844 else
5845 {
5846 // Insert the new object after 'child'
5847 if (node->GetNext())
5848 m_children.Insert(node->GetNext(), newObject);
5849 else
5850 m_children.Append(newObject);
5851 newObject->SetParent(this);
5852
5853 if (previousObject)
5854 *previousObject = child;
5855
5856 return newObject;
5857 }
5858 }
5859
5860 node = node->GetNext();
5861 }
5862 if (previousObject)
5863 *previousObject = NULL;
5864 return NULL;
5865}
5866
5867/// Move content to a list from obj on
5868void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5869{
5870 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5871 while (node)
5872 {
5873 wxRichTextObject* child = node->GetData();
5874 list.Append(child);
5875
5876 wxRichTextObjectList::compatibility_iterator oldNode = node;
5877
5878 node = node->GetNext();
5879
5880 m_children.DeleteNode(oldNode);
5881 }
5882}
5883
5884/// Add content back from list
5885void wxRichTextParagraph::MoveFromList(wxList& list)
5886{
09f14108 5887 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5888 {
5889 AppendChild((wxRichTextObject*) node->GetData());
5890 }
5891}
5892
5893/// Calculate range
5894void wxRichTextParagraph::CalculateRange(long start, long& end)
5895{
5896 wxRichTextCompositeObject::CalculateRange(start, end);
5897
5898 // Add one for end of paragraph
5899 end ++;
5900
5901 m_range.SetRange(start, end);
5902}
5903
5904/// Find the object at the given position
5905wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5906{
5907 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5908 while (node)
5909 {
5910 wxRichTextObject* obj = node->GetData();
603f702b
JS
5911 if (obj->GetRange().Contains(position) ||
5912 obj->GetRange().GetStart() == position ||
5913 obj->GetRange().GetEnd() == position)
5d7836c4 5914 return obj;
7fe8059f 5915
5d7836c4
JS
5916 node = node->GetNext();
5917 }
5918 return NULL;
5919}
5920
5921/// Get the plain text searching from the start or end of the range.
5922/// The resulting string may be shorter than the range given.
5923bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5924{
5925 text = wxEmptyString;
5926
5927 if (fromStart)
5928 {
5929 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5930 while (node)
5931 {
5932 wxRichTextObject* obj = node->GetData();
5933 if (!obj->GetRange().IsOutside(range))
5934 {
5935 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5936 if (textObj)
5937 {
5938 text += textObj->GetTextForRange(range);
5939 }
5940 else
043c0d58
JS
5941 {
5942 text += wxT(" ");
5943 }
5d7836c4
JS
5944 }
5945
5946 node = node->GetNext();
5947 }
5948 }
5949 else
5950 {
5951 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5952 while (node)
5953 {
5954 wxRichTextObject* obj = node->GetData();
5955 if (!obj->GetRange().IsOutside(range))
5956 {
5957 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5958 if (textObj)
5959 {
5960 text = textObj->GetTextForRange(range) + text;
5961 }
5962 else
043c0d58
JS
5963 {
5964 text = wxT(" ") + text;
5965 }
5d7836c4
JS
5966 }
5967
5968 node = node->GetPrevious();
5969 }
5970 }
5971
5972 return true;
5973}
5974
5975/// Find a suitable wrap position.
8db2e3ef 5976bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5977{
72945e24
JS
5978 if (range.GetLength() <= 0)
5979 return false;
5980
5d7836c4
JS
5981 // Find the first position where the line exceeds the available space.
5982 wxSize sz;
5d7836c4 5983 long breakPosition = range.GetEnd();
ecb5fbf1 5984
31778480
JS
5985#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5986 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 5987 {
31778480 5988 int widthBefore;
5d7836c4 5989
31778480
JS
5990 if (range.GetStart() > GetRange().GetStart())
5991 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5992 else
5993 widthBefore = 0;
5994
5995 size_t i;
43a0d1e1 5996 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 5997 {
31778480 5998 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 5999
72945e24 6000 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 6001 {
31778480
JS
6002 breakPosition = i-1;
6003 break;
ecb5fbf1 6004 }
5d7836c4 6005 }
31778480
JS
6006 }
6007 else
6008#endif
6009 {
6010 // Binary chop for speed
6011 long minPos = range.GetStart();
6012 long maxPos = range.GetEnd();
6013 while (true)
ecb5fbf1 6014 {
31778480
JS
6015 if (minPos == maxPos)
6016 {
6017 int descent = 0;
8db2e3ef 6018 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 6019
31778480
JS
6020 if (sz.x > availableSpace)
6021 breakPosition = minPos - 1;
6022 break;
6023 }
6024 else if ((maxPos - minPos) == 1)
ecb5fbf1 6025 {
31778480 6026 int descent = 0;
8db2e3ef 6027 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6028
6029 if (sz.x > availableSpace)
6030 breakPosition = minPos - 1;
6031 else
6032 {
8db2e3ef 6033 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6034 if (sz.x > availableSpace)
6035 breakPosition = maxPos-1;
6036 }
6037 break;
ecb5fbf1
JS
6038 }
6039 else
6040 {
31778480
JS
6041 long nextPos = minPos + ((maxPos - minPos) / 2);
6042
6043 int descent = 0;
8db2e3ef 6044 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6045
6046 if (sz.x > availableSpace)
6047 {
6048 maxPos = nextPos;
6049 }
6050 else
6051 {
6052 minPos = nextPos;
6053 }
ecb5fbf1
JS
6054 }
6055 }
5d7836c4
JS
6056 }
6057
6058 // Now we know the last position on the line.
6059 // Let's try to find a word break.
6060
6061 wxString plainText;
6062 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6063 {
ff76711f
JS
6064 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6065 if (newLinePos != wxNOT_FOUND)
5d7836c4 6066 {
ff76711f
JS
6067 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6068 }
6069 else
6070 {
6071 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
6072 int tabPos = plainText.Find(wxT('\t'), true);
6073 int pos = wxMax(spacePos, tabPos);
6074 if (pos != wxNOT_FOUND)
ff76711f 6075 {
31002e44 6076 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
6077 breakPosition = breakPosition - positionsFromEndOfString;
6078 }
5d7836c4
JS
6079 }
6080 }
6081
6082 wrapPosition = breakPosition;
6083
6084 return true;
6085}
6086
6087/// Get the bullet text for this paragraph.
6088wxString wxRichTextParagraph::GetBulletText()
6089{
6090 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6091 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6092 return wxEmptyString;
6093
6094 int number = GetAttributes().GetBulletNumber();
6095
6096 wxString text;
d2d0adc7 6097 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
6098 {
6099 text.Printf(wxT("%d"), number);
6100 }
6101 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6102 {
6103 // TODO: Unicode, and also check if number > 26
6104 text.Printf(wxT("%c"), (wxChar) (number+64));
6105 }
6106 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6107 {
6108 // TODO: Unicode, and also check if number > 26
6109 text.Printf(wxT("%c"), (wxChar) (number+96));
6110 }
6111 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6112 {
59509217 6113 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
6114 }
6115 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6116 {
59509217
JS
6117 text = wxRichTextDecimalToRoman(number);
6118 text.MakeLower();
5d7836c4
JS
6119 }
6120 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6121 {
d2d0adc7
JS
6122 text = GetAttributes().GetBulletText();
6123 }
3e541562 6124
d2d0adc7
JS
6125 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6126 {
6127 // The outline style relies on the text being computed statically,
6128 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6129 // should be stored in the attributes; if not, just use the number for this
6130 // level, as previously computed.
6131 if (!GetAttributes().GetBulletText().IsEmpty())
6132 text = GetAttributes().GetBulletText();
5d7836c4
JS
6133 }
6134
6135 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6136 {
6137 text = wxT("(") + text + wxT(")");
6138 }
d2d0adc7
JS
6139 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6140 {
6141 text = text + wxT(")");
6142 }
6143
5d7836c4
JS
6144 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6145 {
6146 text += wxT(".");
6147 }
6148
6149 return text;
6150}
6151
1e967276
JS
6152/// Allocate or reuse a line object
6153wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6154{
6155 if (pos < (int) m_cachedLines.GetCount())
6156 {
6157 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6158 line->Init(this);
6159 return line;
6160 }
6161 else
6162 {
6163 wxRichTextLine* line = new wxRichTextLine(this);
6164 m_cachedLines.Append(line);
6165 return line;
6166 }
6167}
6168
6169/// Clear remaining unused line objects, if any
6170bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6171{
6172 int cachedLineCount = m_cachedLines.GetCount();
6173 if ((int) cachedLineCount > lineCount)
6174 {
6175 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6176 {
6177 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6178 wxRichTextLine* line = node->GetData();
6179 m_cachedLines.Erase(node);
6180 delete line;
6181 }
6182 }
6183 return true;
6184}
6185
fe5aa22c
JS
6186/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6187/// retrieve the actual style.
603f702b 6188wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 6189{
24777478 6190 wxRichTextAttr attr;
603f702b 6191 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6192 if (buf)
6193 {
6194 attr = buf->GetBasicStyle();
603f702b
JS
6195 if (!includingBoxAttr)
6196 {
6197 attr.GetTextBoxAttr().Reset();
6198 // The background colour will be painted by the container, and we don't
6199 // want to unnecessarily overwrite the background when we're drawing text
6200 // because this may erase the guideline (which appears just under the text
6201 // if there's no padding).
6202 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6203 }
fe5aa22c
JS
6204 wxRichTextApplyStyle(attr, GetAttributes());
6205 }
6206 else
6207 attr = GetAttributes();
6208
6209 wxRichTextApplyStyle(attr, contentStyle);
6210 return attr;
6211}
6212
6213/// Get combined attributes of the base style and paragraph style.
603f702b 6214wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 6215{
24777478 6216 wxRichTextAttr attr;
603f702b 6217 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6218 if (buf)
6219 {
6220 attr = buf->GetBasicStyle();
603f702b
JS
6221 if (!includingBoxAttr)
6222 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
6223 wxRichTextApplyStyle(attr, GetAttributes());
6224 }
6225 else
6226 attr = GetAttributes();
6227
6228 return attr;
6229}
5d7836c4 6230
603f702b 6231// Create default tabstop array
cfa3b256
JS
6232void wxRichTextParagraph::InitDefaultTabs()
6233{
6234 // create a default tab list at 10 mm each.
6235 for (int i = 0; i < 20; ++i)
6236 {
6237 sm_defaultTabs.Add(i*100);
6238 }
6239}
6240
603f702b 6241// Clear default tabstop array
cfa3b256
JS
6242void wxRichTextParagraph::ClearDefaultTabs()
6243{
6244 sm_defaultTabs.Clear();
6245}
6246
c4168888 6247void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
6248{
6249 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6250 while (node)
6251 {
bec80f4f 6252 wxRichTextObject* anchored = node->GetData();
07d4142f 6253 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652 6254 {
c4168888
JS
6255 int x = 0;
6256 wxRichTextAttr parentAttr(GetAttributes());
6257 context.ApplyVirtualAttributes(parentAttr, this);
6258#if 1
6259 // 27-09-2012
6260 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6261
6262 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6263 parentAttr, anchored->GetAttributes(),
6264 parentRect, availableSpace,
6265 style);
6266 wxSize size = anchored->GetCachedSize();
6267#else
cdaed652 6268 wxSize size;
c4168888 6269 int descent = 0;
8db2e3ef 6270 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
c4168888 6271#endif
bec80f4f 6272
24777478 6273 int offsetY = 0;
603f702b 6274 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
6275 {
6276 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6277 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6278 {
6279 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6280 }
6281 }
bec80f4f 6282
24777478 6283 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6284
cdaed652 6285 /* Update the offset */
24777478
JS
6286 int newOffsetY = pos - rect.y;
6287 if (newOffsetY != offsetY)
6288 {
6289 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6290 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6291 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6292 }
cdaed652 6293
24777478 6294 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6295 x = rect.x;
24777478 6296 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6297 x = rect.x + rect.width - size.x;
24777478 6298
c4168888
JS
6299 //anchored->SetPosition(wxPoint(x, pos));
6300 anchored->Move(wxPoint(x, pos)); // should move children
cdaed652
VZ
6301 anchored->SetCachedSize(size);
6302 floatCollector->CollectFloat(this, anchored);
6303 }
6304
6305 node = node->GetNext();
6306 }
6307}
6308
603f702b 6309// Get the first position from pos that has a line break character.
ff76711f
JS
6310long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6311{
6312 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6313 while (node)
6314 {
6315 wxRichTextObject* obj = node->GetData();
6316 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6317 {
6318 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6319 if (textObj)
6320 {
6321 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6322 if (breakPos > -1)
6323 return breakPos;
6324 }
6325 }
6326 node = node->GetNext();
6327 }
6328 return -1;
6329}
cfa3b256 6330
5d7836c4
JS
6331/*!
6332 * wxRichTextLine
6333 * This object represents a line in a paragraph, and stores
6334 * offsets from the start of the paragraph representing the
6335 * start and end positions of the line.
6336 */
6337
6338wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6339{
1e967276 6340 Init(parent);
5d7836c4
JS
6341}
6342
6343/// Initialisation
1e967276 6344void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6345{
1e967276
JS
6346 m_parent = parent;
6347 m_range.SetRange(-1, -1);
6348 m_pos = wxPoint(0, 0);
6349 m_size = wxSize(0, 0);
5d7836c4 6350 m_descent = 0;
2f45f554
JS
6351#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6352 m_objectSizes.Clear();
6353#endif
5d7836c4
JS
6354}
6355
6356/// Copy
6357void wxRichTextLine::Copy(const wxRichTextLine& obj)
6358{
6359 m_range = obj.m_range;
2f45f554
JS
6360#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6361 m_objectSizes = obj.m_objectSizes;
6362#endif
5d7836c4
JS
6363}
6364
6365/// Get the absolute object position
6366wxPoint wxRichTextLine::GetAbsolutePosition() const
6367{
6368 return m_parent->GetPosition() + m_pos;
6369}
6370
1e967276
JS
6371/// Get the absolute range
6372wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6373{
6374 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6375 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6376 return range;
6377}
6378
5d7836c4
JS
6379/*!
6380 * wxRichTextPlainText
6381 * This object represents a single piece of text.
6382 */
6383
6384IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6385
24777478 6386wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6387 wxRichTextObject(parent)
6388{
5d7836c4
JS
6389 if (style)
6390 SetAttributes(*style);
6391
6392 m_text = text;
6393}
6394
cfa3b256
JS
6395#define USE_KERNING_FIX 1
6396
4794d69c
JS
6397// If insufficient tabs are defined, this is the tab width used
6398#define WIDTH_FOR_DEFAULT_TABS 50
6399
5d7836c4 6400/// Draw the item
8db2e3ef 6401bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6402{
fe5aa22c
JS
6403 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6404 wxASSERT (para != NULL);
6405
603f702b 6406 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6407 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6408
6409 // Let's make the assumption for now that for content in a paragraph, including
6410 // text, we never have a discontinuous selection. So we only deal with a
6411 // single range.
6412 wxRichTextRange selectionRange;
6413 if (selection.IsValid())
6414 {
6415 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6416 if (selectionRanges.GetCount() > 0)
6417 selectionRange = selectionRanges[0];
6418 else
6419 selectionRange = wxRICHTEXT_NO_SELECTION;
6420 }
6421 else
6422 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6423
5d7836c4
JS
6424 int offset = GetRange().GetStart();
6425
ff76711f 6426 wxString str = m_text;
f7667b84
JS
6427 if (context.HasVirtualText(this))
6428 {
6429 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6430 str = m_text;
6431 }
6432
6433 // Replace line break characters with spaces
ff76711f
JS
6434 wxString toRemove = wxRichTextLineBreakChar;
6435 str.Replace(toRemove, wxT(" "));
d07f2e19 6436 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
c025e094 6437 str.MakeUpper();
3e541562 6438
5d7836c4 6439 long len = range.GetLength();
ff76711f 6440 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6441
5d7836c4
JS
6442 // Test for the optimized situations where all is selected, or none
6443 // is selected.
6444
30bf7630
JS
6445 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6446 wxCheckSetFont(dc, textFont);
6447 int charHeight = dc.GetCharHeight();
6448
6449 int x, y;
a1b806b9 6450 if ( textFont.IsOk() )
30bf7630 6451 {
d07f2e19
JS
6452 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6453 {
6454 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6455 wxCheckSetFont(dc, textFont);
6456 charHeight = dc.GetCharHeight();
6457 }
6458
30bf7630
JS
6459 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6460 {
32423dd8
JS
6461 if (textFont.IsUsingSizeInPixels())
6462 {
6463 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
4ba36292 6464 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
32423dd8
JS
6465 x = rect.x;
6466 y = rect.y;
6467 }
6468 else
6469 {
6470 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6471 textFont.SetPointSize(static_cast<int>(size));
32423dd8
JS
6472 x = rect.x;
6473 y = rect.y;
6474 }
30bf7630
JS
6475 wxCheckSetFont(dc, textFont);
6476 }
6477 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6478 {
32423dd8
JS
6479 if (textFont.IsUsingSizeInPixels())
6480 {
6481 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6482 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6483 x = rect.x;
4ba36292 6484 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6485 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6486 }
6487 else
6488 {
6489 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6490 textFont.SetPointSize(static_cast<int>(size));
32423dd8 6491 x = rect.x;
4ba36292 6492 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6493 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6494 }
30bf7630
JS
6495 wxCheckSetFont(dc, textFont);
6496 }
6497 else
6498 {
6499 x = rect.x;
6500 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6501 }
6502 }
6503 else
6504 {
6505 x = rect.x;
6506 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6507 }
5d7836c4 6508
603f702b
JS
6509 // TODO: new selection code
6510
5d7836c4
JS
6511 // (a) All selected.
6512 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6513 {
fe5aa22c 6514 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6515 }
6516 // (b) None selected.
6517 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6518 {
6519 // Draw all unselected
fe5aa22c 6520 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6521 }
6522 else
6523 {
6524 // (c) Part selected, part not
6525 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6526
04ee05f9 6527 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6528
5d7836c4
JS
6529 // 1. Initial unselected chunk, if any, up until start of selection.
6530 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6531 {
6532 int r1 = range.GetStart();
6533 int s1 = selectionRange.GetStart()-1;
6534 int fragmentLen = s1 - r1 + 1;
6535 if (fragmentLen < 0)
af588446 6536 {
5d7836c4 6537 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6538 }
ff76711f 6539 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6540
fe5aa22c 6541 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6542
6543#if USE_KERNING_FIX
6544 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6545 {
6546 // Compensate for kerning difference
ff76711f
JS
6547 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6548 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6549
cfa3b256
JS
6550 wxCoord w1, h1, w2, h2, w3, h3;
6551 dc.GetTextExtent(stringFragment, & w1, & h1);
6552 dc.GetTextExtent(stringFragment2, & w2, & h2);
6553 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6554
cfa3b256
JS
6555 int kerningDiff = (w1 + w3) - w2;
6556 x = x - kerningDiff;
6557 }
6558#endif
5d7836c4
JS
6559 }
6560
6561 // 2. Selected chunk, if any.
6562 if (selectionRange.GetEnd() >= range.GetStart())
6563 {
6564 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6565 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6566
6567 int fragmentLen = s2 - s1 + 1;
6568 if (fragmentLen < 0)
af588446 6569 {
5d7836c4 6570 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6571 }
ff76711f 6572 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6573
fe5aa22c 6574 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6575
6576#if USE_KERNING_FIX
6577 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6578 {
6579 // Compensate for kerning difference
ff76711f
JS
6580 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6581 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6582
cfa3b256
JS
6583 wxCoord w1, h1, w2, h2, w3, h3;
6584 dc.GetTextExtent(stringFragment, & w1, & h1);
6585 dc.GetTextExtent(stringFragment2, & w2, & h2);
6586 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6587
cfa3b256
JS
6588 int kerningDiff = (w1 + w3) - w2;
6589 x = x - kerningDiff;
6590 }
6591#endif
5d7836c4
JS
6592 }
6593
6594 // 3. Remaining unselected chunk, if any
6595 if (selectionRange.GetEnd() < range.GetEnd())
6596 {
6597 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6598 int r2 = range.GetEnd();
6599
6600 int fragmentLen = r2 - s2 + 1;
6601 if (fragmentLen < 0)
af588446 6602 {
5d7836c4 6603 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6604 }
ff76711f 6605 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6606
fe5aa22c 6607 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6608 }
5d7836c4
JS
6609 }
6610
6611 return true;
6612}
61399247 6613
24777478 6614bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6615{
cfa3b256
JS
6616 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6617
6618 wxArrayInt tabArray;
6619 int tabCount;
6620 if (hasTabs)
ab14c7aa 6621 {
cfa3b256
JS
6622 if (attr.GetTabs().IsEmpty())
6623 tabArray = wxRichTextParagraph::GetDefaultTabs();
6624 else
6625 tabArray = attr.GetTabs();
6626 tabCount = tabArray.GetCount();
6627
6628 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6629 {
cfa3b256
JS
6630 int pos = tabArray[i];
6631 pos = ConvertTenthsMMToPixels(dc, pos);
6632 tabArray[i] = pos;
7f0d9d71
JS
6633 }
6634 }
cfa3b256
JS
6635 else
6636 tabCount = 0;
ab14c7aa 6637
cfa3b256
JS
6638 int nextTabPos = -1;
6639 int tabPos = -1;
7f0d9d71 6640 wxCoord w, h;
ab14c7aa 6641
cfa3b256 6642 if (selected)
ab14c7aa 6643 {
0ec6da02
JS
6644 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6645 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6646
ecb5fbf1
JS
6647 wxCheckSetBrush(dc, wxBrush(highlightColour));
6648 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6649 dc.SetTextForeground(highlightTextColour);
04ee05f9 6650 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6651 }
ab14c7aa
JS
6652 else
6653 {
fe5aa22c 6654 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6655
f0e9eda2
JS
6656 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6657 {
04ee05f9 6658 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6659 dc.SetTextBackground(attr.GetBackgroundColour());
6660 }
6661 else
04ee05f9 6662 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6663 }
3e541562 6664
925a662a 6665 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6666 while (hasTabs)
ab14c7aa
JS
6667 {
6668 // the string has a tab
7f0d9d71
JS
6669 // break up the string at the Tab
6670 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6671 str = str.AfterFirst(wxT('\t'));
6672 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6673 tabPos = x + w;
7f0d9d71 6674 bool not_found = true;
cfa3b256 6675 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6676 {
015d0446 6677 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6678
6679 // Find the next tab position.
6680 // Even if we're at the end of the tab array, we must still draw the chunk.
6681
6682 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6683 {
4794d69c
JS
6684 if (nextTabPos <= tabPos)
6685 {
6686 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6687 nextTabPos = tabPos + defaultTabWidth;
6688 }
6689
7f0d9d71 6690 not_found = false;
ab14c7aa
JS
6691 if (selected)
6692 {
cfa3b256 6693 w = nextTabPos - x;
7f0d9d71 6694 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6695 dc.DrawRectangle(selRect);
7f0d9d71
JS
6696 }
6697 dc.DrawText(stringChunk, x, y);
42688aea
JS
6698
6699 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6700 {
6701 wxPen oldPen = dc.GetPen();
ecb5fbf1 6702 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6703 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6704 wxCheckSetPen(dc, oldPen);
42688aea
JS
6705 }
6706
cfa3b256 6707 x = nextTabPos;
7f0d9d71
JS
6708 }
6709 }
cfa3b256 6710 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6711 }
61399247 6712
cfa3b256 6713 if (!str.IsEmpty())
ab14c7aa 6714 {
cfa3b256
JS
6715 dc.GetTextExtent(str, & w, & h);
6716 if (selected)
6717 {
6718 wxRect selRect(x, rect.y, w, rect.GetHeight());
6719 dc.DrawRectangle(selRect);
6720 }
6721 dc.DrawText(str, x, y);
42688aea
JS
6722
6723 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6724 {
6725 wxPen oldPen = dc.GetPen();
ecb5fbf1 6726 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6727 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6728 wxCheckSetPen(dc, oldPen);
42688aea
JS
6729 }
6730
cfa3b256 6731 x += w;
7f0d9d71 6732 }
5d7836c4 6733
7c9fdebe 6734 return true;
7f0d9d71 6735}
fe5aa22c 6736
5d7836c4 6737/// Lay the item out
8db2e3ef 6738bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6739{
ecb5fbf1
JS
6740 // Only lay out if we haven't already cached the size
6741 if (m_size.x == -1)
8db2e3ef 6742 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6743 m_maxSize = m_size;
6744 // Eventually we want to have a reasonable estimate of minimum size.
6745 m_minSize = wxSize(0, 0);
5d7836c4
JS
6746 return true;
6747}
6748
6749/// Copy
6750void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6751{
6752 wxRichTextObject::Copy(obj);
6753
6754 m_text = obj.m_text;
6755}
6756
6757/// Get/set the object size for the given range. Returns false if the range
6758/// is invalid for this object.
914a4e23 6759bool 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
6760{
6761 if (!range.IsWithin(GetRange()))
6762 return false;
6763
fe5aa22c
JS
6764 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6765 wxASSERT (para != NULL);
603f702b 6766
925a662a 6767 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6768
24777478 6769 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6770 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6771
5d7836c4
JS
6772 // Always assume unformatted text, since at this level we have no knowledge
6773 // of line breaks - and we don't need it, since we'll calculate size within
6774 // formatted text by doing it in chunks according to the line ranges
6775
30bf7630 6776 bool bScript(false);
44cc96a8 6777 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6778 if (font.IsOk())
30bf7630
JS
6779 {
6780 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6781 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6782 {
6783 wxFont textFont = font;
32423dd8
JS
6784 if (textFont.IsUsingSizeInPixels())
6785 {
6786 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6787 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6788 }
6789 else
6790 {
6791 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6792 textFont.SetPointSize(static_cast<int>(size));
6793 }
30bf7630
JS
6794 wxCheckSetFont(dc, textFont);
6795 bScript = true;
6796 }
d07f2e19
JS
6797 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6798 {
6799 wxFont textFont = font;
6800 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6801 wxCheckSetFont(dc, textFont);
6802 bScript = true;
6803 }
30bf7630
JS
6804 else
6805 {
6806 wxCheckSetFont(dc, font);
6807 }
6808 }
5d7836c4 6809
109bfc88 6810 bool haveDescent = false;
5d7836c4
JS
6811 int startPos = range.GetStart() - GetRange().GetStart();
6812 long len = range.GetLength();
3e541562 6813
ff76711f 6814 wxString str(m_text);
f7667b84
JS
6815 if (context.HasVirtualText(this))
6816 {
6817 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6818 str = m_text;
6819 }
6820
ff76711f
JS
6821 wxString toReplace = wxRichTextLineBreakChar;
6822 str.Replace(toReplace, wxT(" "));
6823
6824 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea 6825
d07f2e19 6826 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
42688aea
JS
6827 stringChunk.MakeUpper();
6828
5d7836c4 6829 wxCoord w, h;
7f0d9d71 6830 int width = 0;
cfa3b256 6831 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6832 {
6833 // the string has a tab
cfa3b256
JS
6834 wxArrayInt tabArray;
6835 if (textAttr.GetTabs().IsEmpty())
6836 tabArray = wxRichTextParagraph::GetDefaultTabs();
6837 else
6838 tabArray = textAttr.GetTabs();
ab14c7aa 6839
cfa3b256 6840 int tabCount = tabArray.GetCount();
41a85215 6841
cfa3b256 6842 for (int i = 0; i < tabCount; ++i)
61399247 6843 {
cfa3b256
JS
6844 int pos = tabArray[i];
6845 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6846 tabArray[i] = pos;
7f0d9d71 6847 }
41a85215 6848
cfa3b256 6849 int nextTabPos = -1;
61399247 6850
ab14c7aa
JS
6851 while (stringChunk.Find(wxT('\t')) >= 0)
6852 {
109bfc88
JS
6853 int absoluteWidth = 0;
6854
ab14c7aa 6855 // the string has a tab
7f0d9d71
JS
6856 // break up the string at the Tab
6857 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6858 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6859
31778480
JS
6860 if (partialExtents)
6861 {
109bfc88
JS
6862 int oldWidth;
6863 if (partialExtents->GetCount() > 0)
6864 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6865 else
6866 oldWidth = 0;
6867
31778480
JS
6868 // Add these partial extents
6869 wxArrayInt p;
6870 dc.GetPartialTextExtents(stringFragment, p);
6871 size_t j;
6872 for (j = 0; j < p.GetCount(); j++)
6873 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6874
6875 if (partialExtents->GetCount() > 0)
925a662a 6876 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6877 else
925a662a 6878 absoluteWidth = relativeX;
109bfc88
JS
6879 }
6880 else
6881 {
6882 dc.GetTextExtent(stringFragment, & w, & h);
6883 width += w;
603f702b 6884 absoluteWidth = width + relativeX;
109bfc88 6885 haveDescent = true;
31778480
JS
6886 }
6887
cfa3b256
JS
6888 bool notFound = true;
6889 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6890 {
cfa3b256 6891 nextTabPos = tabArray.Item(i);
4794d69c
JS
6892
6893 // Find the next tab position.
6894 // Even if we're at the end of the tab array, we must still process the chunk.
6895
6896 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6897 {
4794d69c
JS
6898 if (nextTabPos <= absoluteWidth)
6899 {
6900 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6901 nextTabPos = absoluteWidth + defaultTabWidth;
6902 }
6903
cfa3b256 6904 notFound = false;
925a662a 6905 width = nextTabPos - relativeX;
31778480
JS
6906
6907 if (partialExtents)
6908 partialExtents->Add(width);
7f0d9d71
JS
6909 }
6910 }
6911 }
6912 }
30bf7630 6913
31778480
JS
6914 if (!stringChunk.IsEmpty())
6915 {
31778480
JS
6916 if (partialExtents)
6917 {
109bfc88
JS
6918 int oldWidth;
6919 if (partialExtents->GetCount() > 0)
6920 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6921 else
6922 oldWidth = 0;
6923
31778480
JS
6924 // Add these partial extents
6925 wxArrayInt p;
6926 dc.GetPartialTextExtents(stringChunk, p);
6927 size_t j;
6928 for (j = 0; j < p.GetCount(); j++)
6929 partialExtents->Add(oldWidth + p[j]);
6930 }
109bfc88
JS
6931 else
6932 {
6933 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6934 width += w;
6935 haveDescent = true;
6936 }
6937 }
6938
6939 if (partialExtents)
6940 {
6941 int charHeight = dc.GetCharHeight();
6942 if ((*partialExtents).GetCount() > 0)
6943 w = (*partialExtents)[partialExtents->GetCount()-1];
6944 else
6945 w = 0;
6946 size = wxSize(w, charHeight);
6947 }
6948 else
6949 {
6950 size = wxSize(width, dc.GetCharHeight());
31778480 6951 }
30bf7630 6952
109bfc88
JS
6953 if (!haveDescent)
6954 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6955
30bf7630
JS
6956 if ( bScript )
6957 dc.SetFont(font);
6958
5d7836c4
JS
6959 return true;
6960}
6961
6962/// Do a split, returning an object containing the second part, and setting
6963/// the first part in 'this'.
6964wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6965{
ff76711f 6966 long index = pos - GetRange().GetStart();
3e541562 6967
28f92d74 6968 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6969 return NULL;
6970
6971 wxString firstPart = m_text.Mid(0, index);
6972 wxString secondPart = m_text.Mid(index);
6973
6974 m_text = firstPart;
6975
6976 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6977 newObject->SetAttributes(GetAttributes());
8db2e3ef 6978 newObject->SetProperties(GetProperties());
5d7836c4
JS
6979
6980 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6981 GetRange().SetEnd(pos-1);
3e541562 6982
5d7836c4
JS
6983 return newObject;
6984}
6985
6986/// Calculate range
6987void wxRichTextPlainText::CalculateRange(long start, long& end)
6988{
28f92d74 6989 end = start + m_text.length() - 1;
5d7836c4
JS
6990 m_range.SetRange(start, end);
6991}
6992
6993/// Delete range
6994bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6995{
6996 wxRichTextRange r = range;
6997
6998 r.LimitTo(GetRange());
6999
7000 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
7001 {
7002 m_text.Empty();
7003 return true;
7004 }
7005
7006 long startIndex = r.GetStart() - GetRange().GetStart();
7007 long len = r.GetLength();
7008
7009 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
7010 return true;
7011}
7012
7013/// Get text for the given range.
7014wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
7015{
7016 wxRichTextRange r = range;
7017
7018 r.LimitTo(GetRange());
7019
7020 long startIndex = r.GetStart() - GetRange().GetStart();
7021 long len = r.GetLength();
7022
7023 return m_text.Mid(startIndex, len);
7024}
7025
7026/// Returns true if this object can merge itself with the given one.
f7667b84 7027bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
5d7836c4 7028{
f7667b84
JS
7029 // JACS 2013-01-27
7030 if (!context.GetVirtualAttributesEnabled())
7031 {
7032 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
7033 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
7034 }
7035 else
7036 {
7037 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7038 if (!otherObj || m_text.empty())
7039 return false;
7040
7041 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7042 return false;
7043
7044 // Check if differing virtual attributes makes it impossible to merge
7045 // these strings.
7046
7047 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7048 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7049 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7050 return true;
7051 else if (hasVirtualAttr1 != hasVirtualAttr2)
7052 return false;
7053 else
7054 {
7055 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7056 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7057 return virtualAttr1 == virtualAttr2;
7058 }
7059 }
5d7836c4
JS
7060}
7061
7062/// Returns true if this object merged itself with the given one.
7063/// The calling code will then delete the given object.
f7667b84 7064bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
5d7836c4
JS
7065{
7066 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7067 wxASSERT( textObject != NULL );
7068
7069 if (textObject)
7070 {
7071 m_text += textObject->GetText();
99404ab0 7072 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
7073 return true;
7074 }
7075 else
7076 return false;
7077}
7078
f7667b84
JS
7079bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7080{
7081 // If this object has any virtual attributes at all, whether for the whole object
7082 // or individual ones, we should try splitting it by calling Split.
7083 // Must be more than one character in order to be able to split.
7084 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7085}
7086
7087wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7088{
7089 int count = context.GetVirtualSubobjectAttributesCount(this);
7090 if (count > 0 && GetParent())
7091 {
7092 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7093 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7094 if (node)
7095 {
7096 const wxRichTextAttr emptyAttr;
7097 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7098
7099 wxArrayInt positions;
7100 wxRichTextAttrArray attributes;
7101 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7102 {
7103 wxASSERT(positions.GetCount() == attributes.GetCount());
7104
7105 // We will gather up runs of text with the same virtual attributes
7106
7107 int len = m_text.Length();
7108 int i = 0;
7109
7110 // runStart and runEnd represent the accumulated run with a consistent attribute
7111 // that hasn't yet been appended
7112 int runStart = -1;
7113 int runEnd = -1;
7114 wxRichTextAttr currentAttr;
7115 wxString text = m_text;
7116 wxRichTextPlainText* lastPlainText = this;
7117
7118 for (i = 0; i < (int) positions.GetCount(); i++)
7119 {
7120 int pos = positions[i];
7121 wxASSERT(pos >= 0 && pos < len);
7122 if (pos >= 0 && pos < len)
7123 {
7124 const wxRichTextAttr& attr = attributes[i];
7125
7126 if (pos == 0)
7127 {
7128 runStart = 0;
7129 currentAttr = attr;
7130 }
7131 // Check if there was a gap from the last known attribute and this.
7132 // In that case, we need to do something with the span of non-attributed text.
7133 else if ((pos-1) > runEnd)
7134 {
7135 if (runEnd == -1)
7136 {
7137 // We hadn't processed anything previously, so the previous run is from the text start
7138 // to just before this position. The current attribute remains empty.
7139 runStart = 0;
7140 runEnd = pos-1;
7141 }
7142 else
7143 {
7144 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7145 // then just extend the run.
7146 if (currentAttr.IsDefault())
7147 {
7148 runEnd = pos-1;
7149 }
7150 else
7151 {
7152 // We need to add an object, or reuse the existing one.
7153 if (runStart == 0)
7154 {
7155 lastPlainText = this;
7156 SetText(text.Mid(runStart, runEnd - runStart + 1));
7157 }
7158 else
7159 {
7160 wxRichTextPlainText* obj = new wxRichTextPlainText;
7161 lastPlainText = obj;
7162 obj->SetAttributes(GetAttributes());
7163 obj->SetProperties(GetProperties());
7164 obj->SetParent(parent);
7165
7166 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7167 if (next)
7168 parent->GetChildren().Insert(next, obj);
7169 else
7170 parent->GetChildren().Append(obj);
7171 }
7172
7173 runStart = runEnd+1;
7174 runEnd = pos-1;
7175
7176 currentAttr = emptyAttr;
7177 }
7178 }
7179 }
7180
7181 wxASSERT(runEnd == pos-1);
7182
7183 // Now we only have to deal with the previous run
7184 if (currentAttr == attr)
7185 {
7186 // If we still have the same attributes, then we
7187 // simply increase the run size.
7188 runEnd = pos;
7189 }
7190 else
7191 {
7192 if (runEnd >= 0)
7193 {
7194 // We need to add an object, or reuse the existing one.
7195 if (runStart == 0)
7196 {
7197 lastPlainText = this;
7198 SetText(text.Mid(runStart, runEnd - runStart + 1));
7199 }
7200 else
7201 {
7202 wxRichTextPlainText* obj = new wxRichTextPlainText;
7203 lastPlainText = obj;
7204 obj->SetAttributes(GetAttributes());
7205 obj->SetProperties(GetProperties());
7206 obj->SetParent(parent);
7207
7208 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7209 if (next)
7210 parent->GetChildren().Insert(next, obj);
7211 else
7212 parent->GetChildren().Append(obj);
7213 }
7214 }
7215
7216 runStart = pos;
7217 runEnd = pos;
7218
7219 currentAttr = attr;
7220 }
7221 }
7222 }
7223
7224 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7225 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7226 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7227 {
7228 // If the current attribute is empty, merge the run with the next fragment
7229 // which by definition (because it's not specified) has empty attributes.
7230 if (currentAttr.IsDefault())
7231 runEnd = (len-1);
7232
7233 if (runEnd < (len-1))
7234 {
7235 // We need to add an object, or reuse the existing one.
7236 if (runStart == 0)
7237 {
7238 lastPlainText = this;
7239 SetText(text.Mid(runStart, runEnd - runStart + 1));
7240 }
7241 else
7242 {
7243 wxRichTextPlainText* obj = new wxRichTextPlainText;
7244 lastPlainText = obj;
7245 obj->SetAttributes(GetAttributes());
7246 obj->SetProperties(GetProperties());
7247 obj->SetParent(parent);
7248
7249 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7250 if (next)
7251 parent->GetChildren().Insert(next, obj);
7252 else
7253 parent->GetChildren().Append(obj);
7254 }
7255
7256 runStart = runEnd+1;
7257 runEnd = (len-1);
7258 }
7259
7260 // Now the last, non-attributed fragment at the end, if any
7261 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7262 {
7263 wxASSERT(runStart != 0);
7264
7265 wxRichTextPlainText* obj = new wxRichTextPlainText;
7266 obj->SetAttributes(GetAttributes());
7267 obj->SetProperties(GetProperties());
7268 obj->SetParent(parent);
7269
7270 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7271 if (next)
7272 parent->GetChildren().Insert(next, obj);
7273 else
7274 parent->GetChildren().Append(obj);
7275
7276 lastPlainText = obj;
7277 }
7278 }
7279
7280 return lastPlainText;
7281 }
7282 }
7283 }
7284 return this;
7285}
7286
5d7836c4
JS
7287/// Dump to output stream for debugging
7288void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7289{
7290 wxRichTextObject::Dump(stream);
7291 stream << m_text << wxT("\n");
7292}
7293
ff76711f
JS
7294/// Get the first position from pos that has a line break character.
7295long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7296{
7297 int i;
7298 int len = m_text.length();
7299 int startPos = pos - m_range.GetStart();
7300 for (i = startPos; i < len; i++)
7301 {
7302 wxChar ch = m_text[i];
7303 if (ch == wxRichTextLineBreakChar)
7304 {
7305 return i + m_range.GetStart();
7306 }
7307 }
7308 return -1;
7309}
7310
5d7836c4
JS
7311/*!
7312 * wxRichTextBuffer
7313 * This is a kind of box, used to represent the whole buffer
7314 */
7315
7316IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7317
7c9fdebe
JS
7318wxList wxRichTextBuffer::sm_handlers;
7319wxList wxRichTextBuffer::sm_drawingHandlers;
7320wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7321wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7322int wxRichTextBuffer::sm_bulletRightMargin = 20;
7323float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
e12b91a3 7324bool wxRichTextBuffer::sm_floatingLayoutMode = true;
5d7836c4
JS
7325
7326/// Initialisation
7327void wxRichTextBuffer::Init()
7328{
7329 m_commandProcessor = new wxCommandProcessor;
7330 m_styleSheet = NULL;
7331 m_modified = false;
7332 m_batchedCommandDepth = 0;
7333 m_batchedCommand = NULL;
7334 m_suppressUndo = 0;
d2d0adc7 7335 m_handlerFlags = 0;
44219ff0 7336 m_scale = 1.0;
32423dd8
JS
7337 m_dimensionScale = 1.0;
7338 m_fontScale = 1.0;
f819ed5d 7339 SetMargins(4);
5d7836c4
JS
7340}
7341
7342/// Initialisation
7343wxRichTextBuffer::~wxRichTextBuffer()
7344{
7345 delete m_commandProcessor;
7346 delete m_batchedCommand;
7347
7348 ClearStyleStack();
d2d0adc7 7349 ClearEventHandlers();
5d7836c4
JS
7350}
7351
85d8909b 7352void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 7353{
85d8909b 7354 Reset();
3e541562 7355
5d7836c4 7356 GetCommandProcessor()->ClearCommands();
5d7836c4 7357
5d7836c4 7358 Modify(false);
1e967276 7359 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
7360}
7361
0ca07313
JS
7362void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7363{
7364 wxRichTextParagraphLayoutBox::Copy(obj);
7365
7366 m_styleSheet = obj.m_styleSheet;
7367 m_modified = obj.m_modified;
bec80f4f
JS
7368 m_batchedCommandDepth = 0;
7369 if (m_batchedCommand)
7370 delete m_batchedCommand;
7371 m_batchedCommand = NULL;
0ca07313 7372 m_suppressUndo = obj.m_suppressUndo;
603f702b 7373 m_invalidRange = obj.m_invalidRange;
32423dd8
JS
7374 m_dimensionScale = obj.m_dimensionScale;
7375 m_fontScale = obj.m_fontScale;
0ca07313
JS
7376}
7377
38f833b1
JS
7378/// Push style sheet to top of stack
7379bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7380{
7381 if (m_styleSheet)
7382 styleSheet->InsertSheet(m_styleSheet);
7383
7384 SetStyleSheet(styleSheet);
41a85215 7385
38f833b1
JS
7386 return true;
7387}
7388
7389/// Pop style sheet from top of stack
7390wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7391{
7392 if (m_styleSheet)
7393 {
7394 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7395 m_styleSheet = oldSheet->GetNextSheet();
7396 oldSheet->Unlink();
41a85215 7397
38f833b1
JS
7398 return oldSheet;
7399 }
7400 else
7401 return NULL;
7402}
7403
0ca07313
JS
7404/// Submit command to insert paragraphs
7405bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7406{
4e63bfb9 7407 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
7408}
7409
7410/// Submit command to insert paragraphs
4e63bfb9 7411bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
7412{
7413 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 7414
0ca07313 7415 action->GetNewParagraphs() = paragraphs;
59509217 7416
0ca07313
JS
7417 action->SetPosition(pos);
7418
603f702b 7419 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
7420 if (!paragraphs.GetPartialParagraph())
7421 range.SetEnd(range.GetEnd()+1);
7422
0ca07313 7423 // Set the range we'll need to delete in Undo
99404ab0 7424 action->SetRange(range);
0ca07313 7425
603f702b 7426 buffer->SubmitAction(action);
0ca07313
JS
7427
7428 return true;
7429}
7430
5d7836c4 7431/// Submit command to insert the given text
fe5aa22c 7432bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7433{
4e63bfb9 7434 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
7435}
7436
7437/// Submit command to insert the given text
4e63bfb9 7438bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7439{
7440 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7441
24777478
JS
7442 wxRichTextAttr* p = NULL;
7443 wxRichTextAttr paraAttr;
fe5aa22c
JS
7444 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7445 {
7c081bd2 7446 // Get appropriate paragraph style
603f702b 7447 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
7448 if (!paraAttr.IsDefault())
7449 p = & paraAttr;
7450 }
7451
fe5aa22c 7452 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 7453
603f702b 7454 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 7455
6636ef8d 7456 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
7457 {
7458 // Don't count the newline when undoing
7459 length --;
5d7836c4 7460 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 7461 }
6636ef8d 7462 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 7463 length --;
5d7836c4
JS
7464
7465 action->SetPosition(pos);
7466
7467 // Set the range we'll need to delete in Undo
0ca07313 7468 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 7469
603f702b 7470 buffer->SubmitAction(action);
7fe8059f 7471
5d7836c4
JS
7472 return true;
7473}
7474
7475/// Submit command to insert the given text
fe5aa22c 7476bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7477{
4e63bfb9 7478 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
7479}
7480
7481/// Submit command to insert the given text
4e63bfb9 7482bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7483{
7484 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7485
24777478
JS
7486 wxRichTextAttr* p = NULL;
7487 wxRichTextAttr paraAttr;
fe5aa22c
JS
7488 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7489 {
603f702b 7490 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
7491 if (!paraAttr.IsDefault())
7492 p = & paraAttr;
7493 }
7494
603f702b 7495 wxRichTextAttr attr(buffer->GetDefaultStyle());
32423dd8
JS
7496 // Don't include box attributes such as margins
7497 attr.GetTextBoxAttr().Reset();
7fe8059f
WS
7498
7499 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
7500 action->GetNewParagraphs().AppendChild(newPara);
7501 action->GetNewParagraphs().UpdateRanges();
7502 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
7503 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7504 long pos1 = pos;
7505
6c0ea513
JS
7506 if (p)
7507 newPara->SetAttributes(*p);
7508
c025e094
JS
7509 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7510 {
7511 if (para && para->GetRange().GetEnd() == pos)
7512 pos1 ++;
e2d0875a
JS
7513
7514 // Now see if we need to number the paragraph.
6c0ea513 7515 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
7516 {
7517 wxRichTextAttr numberingAttr;
7518 if (FindNextParagraphNumber(para, numberingAttr))
7519 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7520 }
c025e094
JS
7521 }
7522
5d7836c4
JS
7523 action->SetPosition(pos);
7524
99404ab0 7525 // Use the default character style
603f702b 7526 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
7527 {
7528 // Check whether the default style merely reflects the paragraph/basic style,
7529 // in which case don't apply it.
603f702b 7530 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
32423dd8 7531 defaultStyle.GetTextBoxAttr().Reset();
24777478 7532 wxRichTextAttr toApply;
c025e094
JS
7533 if (para)
7534 {
603f702b 7535 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 7536 wxRichTextAttr newAttr;
c025e094
JS
7537 // This filters out attributes that are accounted for by the current
7538 // paragraph/basic style
7539 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7540 }
7541 else
7542 toApply = defaultStyle;
7543
7544 if (!toApply.IsDefault())
7545 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7546 }
99404ab0 7547
5d7836c4 7548 // Set the range we'll need to delete in Undo
c025e094 7549 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 7550
603f702b 7551 buffer->SubmitAction(action);
7fe8059f 7552
5d7836c4
JS
7553 return true;
7554}
7555
7556/// Submit command to insert the given image
24777478
JS
7557bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7558 const wxRichTextAttr& textAttr)
5d7836c4 7559{
4e63bfb9 7560 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
7561}
7562
7563/// Submit command to insert the given image
4e63bfb9
JS
7564bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7565 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
7566 const wxRichTextAttr& textAttr)
7567{
7568 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7569
24777478
JS
7570 wxRichTextAttr* p = NULL;
7571 wxRichTextAttr paraAttr;
fe5aa22c
JS
7572 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7573 {
603f702b 7574 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
7575 if (!paraAttr.IsDefault())
7576 p = & paraAttr;
7577 }
7578
603f702b 7579 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 7580
32423dd8
JS
7581 // Don't include box attributes such as margins
7582 attr.GetTextBoxAttr().Reset();
7583
5d7836c4 7584 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
7585 if (p)
7586 newPara->SetAttributes(*p);
7587
5d7836c4
JS
7588 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7589 newPara->AppendChild(imageObject);
24777478 7590 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7591 action->GetNewParagraphs().AppendChild(newPara);
7592 action->GetNewParagraphs().UpdateRanges();
7593
7594 action->GetNewParagraphs().SetPartialParagraph(true);
7595
7596 action->SetPosition(pos);
7597
7598 // Set the range we'll need to delete in Undo
7599 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7600
603f702b 7601 buffer->SubmitAction(action);
7fe8059f 7602
5d7836c4
JS
7603 return true;
7604}
7605
cdaed652 7606// Insert an object with no change of it
603f702b
JS
7607wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7608{
4e63bfb9 7609 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7610}
7611
7612// Insert an object with no change of it
4e63bfb9 7613wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7614{
603f702b 7615 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7616
24777478
JS
7617 wxRichTextAttr* p = NULL;
7618 wxRichTextAttr paraAttr;
cdaed652
VZ
7619 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7620 {
603f702b 7621 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7622 if (!paraAttr.IsDefault())
7623 p = & paraAttr;
7624 }
7625
603f702b 7626 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652 7627
32423dd8
JS
7628 // Don't include box attributes such as margins
7629 attr.GetTextBoxAttr().Reset();
7630
cdaed652
VZ
7631 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7632 if (p)
7633 newPara->SetAttributes(*p);
7634
7635 newPara->AppendChild(object);
7636 action->GetNewParagraphs().AppendChild(newPara);
7637 action->GetNewParagraphs().UpdateRanges();
7638
7639 action->GetNewParagraphs().SetPartialParagraph(true);
7640
7641 action->SetPosition(pos);
7642
7643 // Set the range we'll need to delete in Undo
7644 action->SetRange(wxRichTextRange(pos, pos));
7645
603f702b 7646 buffer->SubmitAction(action);
cdaed652 7647
603f702b
JS
7648 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7649 return obj;
cdaed652 7650}
603f702b 7651
7c9fdebe
JS
7652wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7653 const wxRichTextProperties& properties,
7654 wxRichTextCtrl* ctrl, int flags,
7655 const wxRichTextAttr& textAttr)
7656{
7657 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7658
7659 wxRichTextAttr* p = NULL;
7660 wxRichTextAttr paraAttr;
7661 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7662 {
7663 paraAttr = GetStyleForNewParagraph(buffer, pos);
7664 if (!paraAttr.IsDefault())
7665 p = & paraAttr;
7666 }
7667
7668 wxRichTextAttr attr(buffer->GetDefaultStyle());
7669
32423dd8
JS
7670 // Don't include box attributes such as margins
7671 attr.GetTextBoxAttr().Reset();
7672
7c9fdebe
JS
7673 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7674 if (p)
7675 newPara->SetAttributes(*p);
7676
7677 wxRichTextField* fieldObject = new wxRichTextField();
7678 fieldObject->wxRichTextObject::SetProperties(properties);
7679 fieldObject->SetFieldType(fieldType);
7680 fieldObject->SetAttributes(textAttr);
7681 newPara->AppendChild(fieldObject);
7682 action->GetNewParagraphs().AppendChild(newPara);
7683 action->GetNewParagraphs().UpdateRanges();
7684 action->GetNewParagraphs().SetPartialParagraph(true);
7685 action->SetPosition(pos);
7686
7687 // Set the range we'll need to delete in Undo
7688 action->SetRange(wxRichTextRange(pos, pos));
7689
7690 buffer->SubmitAction(action);
7691
7692 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7693 return obj;
7694}
7695
fe5aa22c
JS
7696/// Get the style that is appropriate for a new paragraph at this position.
7697/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7698/// style.
603f702b 7699wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7700{
7701 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7702 if (para)
7703 {
24777478 7704 wxRichTextAttr attr;
d2d0adc7 7705 bool foundAttributes = false;
3e541562 7706
d2d0adc7 7707 // Look for a matching paragraph style
603f702b 7708 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7709 {
603f702b 7710 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7711 if (paraDef)
fe5aa22c 7712 {
caad0109
JS
7713 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7714 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7715 {
603f702b 7716 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7717 if (nextParaDef)
7718 {
7719 foundAttributes = true;
603f702b 7720 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7721 }
7722 }
3e541562 7723
d2d0adc7
JS
7724 // If we didn't find the 'next style', use this style instead.
7725 if (!foundAttributes)
7726 {
7727 foundAttributes = true;
603f702b 7728 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7729 }
fe5aa22c
JS
7730 }
7731 }
e2d0875a
JS
7732
7733 // Also apply list style if present
603f702b 7734 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7735 {
603f702b 7736 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7737 if (listDef)
7738 {
7739 int thisIndent = para->GetAttributes().GetLeftIndent();
7740 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7741
7742 // Apply the overall list style, and item style for this level
603f702b 7743 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7744 wxRichTextApplyStyle(attr, listStyle);
7745 attr.SetOutlineLevel(thisLevel);
7746 if (para->GetAttributes().HasBulletNumber())
7747 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7748 }
34b4899d 7749 }
e2d0875a 7750
d2d0adc7
JS
7751 if (!foundAttributes)
7752 {
7753 attr = para->GetAttributes();
7754 int flags = attr.GetFlags();
fe5aa22c 7755
d2d0adc7
JS
7756 // Eliminate character styles
7757 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7758 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7759 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7760 attr.SetFlags(flags);
7761 }
3e541562 7762
fe5aa22c
JS
7763 return attr;
7764 }
7765 else
24777478 7766 return wxRichTextAttr();
fe5aa22c
JS
7767}
7768
5d7836c4 7769/// Submit command to delete this range
12cc29c5 7770bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7771{
603f702b
JS
7772 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7773}
7774
7775/// Submit command to delete this range
7776bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7777{
7778 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7779
12cc29c5 7780 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7781
7782 // Set the range to delete
7783 action->SetRange(range);
7fe8059f 7784
5d7836c4
JS
7785 // Copy the fragment that we'll need to restore in Undo
7786 CopyFragment(range, action->GetOldParagraphs());
7787
6c0ea513
JS
7788 // See if we're deleting a paragraph marker, in which case we need to
7789 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7790 if (range.GetStart() == range.GetEnd())
5d7836c4 7791 {
6c0ea513
JS
7792 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7793 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7794 {
6c0ea513
JS
7795 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7796 if (nextPara && nextPara != para)
5d7836c4 7797 {
6c0ea513
JS
7798 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7799 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7800 }
7801 }
7802 }
7803
603f702b 7804 buffer->SubmitAction(action);
7fe8059f 7805
5d7836c4
JS
7806 return true;
7807}
7808
7809/// Collapse undo/redo commands
7810bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7811{
7812 if (m_batchedCommandDepth == 0)
7813 {
7814 wxASSERT(m_batchedCommand == NULL);
7815 if (m_batchedCommand)
7816 {
0745f364 7817 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7818 }
7819 m_batchedCommand = new wxRichTextCommand(cmdName);
7820 }
7821
7fe8059f 7822 m_batchedCommandDepth ++;
5d7836c4
JS
7823
7824 return true;
7825}
7826
7827/// Collapse undo/redo commands
7828bool wxRichTextBuffer::EndBatchUndo()
7829{
7830 m_batchedCommandDepth --;
7831
7832 wxASSERT(m_batchedCommandDepth >= 0);
7833 wxASSERT(m_batchedCommand != NULL);
7834
7835 if (m_batchedCommandDepth == 0)
7836 {
0745f364 7837 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7838 m_batchedCommand = NULL;
7839 }
7840
7841 return true;
7842}
7843
7844/// Submit immediately, or delay according to whether collapsing is on
7845bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7846{
cc2aecde
JS
7847 if (action && !action->GetNewParagraphs().IsEmpty())
7848 PrepareContent(action->GetNewParagraphs());
7849
5d7836c4 7850 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7851 {
7852 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7853 cmd->AddAction(action);
7854 cmd->Do();
7855 cmd->GetActions().Clear();
7856 delete cmd;
7857
5d7836c4 7858 m_batchedCommand->AddAction(action);
0745f364 7859 }
5d7836c4
JS
7860 else
7861 {
7862 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7863 cmd->AddAction(action);
7864
7865 // Only store it if we're not suppressing undo.
7866 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7867 }
7868
7869 return true;
7870}
7871
7872/// Begin suppressing undo/redo commands.
7873bool wxRichTextBuffer::BeginSuppressUndo()
7874{
7fe8059f 7875 m_suppressUndo ++;
5d7836c4
JS
7876
7877 return true;
7878}
7879
7880/// End suppressing undo/redo commands.
7881bool wxRichTextBuffer::EndSuppressUndo()
7882{
7fe8059f 7883 m_suppressUndo --;
5d7836c4
JS
7884
7885 return true;
7886}
7887
7888/// Begin using a style
24777478 7889bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7890{
24777478 7891 wxRichTextAttr newStyle(GetDefaultStyle());
32423dd8 7892 newStyle.GetTextBoxAttr().Reset();
5d7836c4
JS
7893
7894 // Save the old default style
32423dd8 7895 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
5d7836c4
JS
7896
7897 wxRichTextApplyStyle(newStyle, style);
7898 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7899
7900 SetDefaultStyle(newStyle);
7901
5d7836c4
JS
7902 return true;
7903}
7904
7905/// End the style
7906bool wxRichTextBuffer::EndStyle()
7907{
63886f6d 7908 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7909 {
7910 wxLogDebug(_("Too many EndStyle calls!"));
7911 return false;
7912 }
7913
09f14108 7914 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7915 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7916 m_attributeStack.Erase(node);
5d7836c4
JS
7917
7918 SetDefaultStyle(*attr);
7919
7920 delete attr;
7921 return true;
7922}
7923
7924/// End all styles
7925bool wxRichTextBuffer::EndAllStyles()
7926{
7927 while (m_attributeStack.GetCount() != 0)
7928 EndStyle();
7929 return true;
7930}
7931
7932/// Clear the style stack
7933void wxRichTextBuffer::ClearStyleStack()
7934{
09f14108 7935 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7936 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7937 m_attributeStack.Clear();
7938}
7939
7940/// Begin using bold
7941bool wxRichTextBuffer::BeginBold()
7942{
24777478 7943 wxRichTextAttr attr;
7d76fbd5 7944 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7945
5d7836c4
JS
7946 return BeginStyle(attr);
7947}
7948
7949/// Begin using italic
7950bool wxRichTextBuffer::BeginItalic()
7951{
24777478 7952 wxRichTextAttr attr;
7d76fbd5 7953 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7954
5d7836c4
JS
7955 return BeginStyle(attr);
7956}
7957
7958/// Begin using underline
7959bool wxRichTextBuffer::BeginUnderline()
7960{
24777478 7961 wxRichTextAttr attr;
44cc96a8 7962 attr.SetFontUnderlined(true);
7fe8059f 7963
5d7836c4
JS
7964 return BeginStyle(attr);
7965}
7966
7967/// Begin using point size
7968bool wxRichTextBuffer::BeginFontSize(int pointSize)
7969{
24777478 7970 wxRichTextAttr attr;
44cc96a8 7971 attr.SetFontSize(pointSize);
7fe8059f 7972
5d7836c4
JS
7973 return BeginStyle(attr);
7974}
7975
7976/// Begin using this font
7977bool wxRichTextBuffer::BeginFont(const wxFont& font)
7978{
24777478 7979 wxRichTextAttr attr;
5d7836c4 7980 attr.SetFont(font);
7fe8059f 7981
5d7836c4
JS
7982 return BeginStyle(attr);
7983}
7984
7985/// Begin using this colour
7986bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7987{
24777478 7988 wxRichTextAttr attr;
5d7836c4
JS
7989 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7990 attr.SetTextColour(colour);
7fe8059f 7991
5d7836c4
JS
7992 return BeginStyle(attr);
7993}
7994
7995/// Begin using alignment
7996bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7997{
24777478 7998 wxRichTextAttr attr;
5d7836c4
JS
7999 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
8000 attr.SetAlignment(alignment);
7fe8059f 8001
5d7836c4
JS
8002 return BeginStyle(attr);
8003}
8004
8005/// Begin left indent
8006bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
8007{
24777478 8008 wxRichTextAttr attr;
5d7836c4
JS
8009 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
8010 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8011
5d7836c4
JS
8012 return BeginStyle(attr);
8013}
8014
8015/// Begin right indent
8016bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
8017{
24777478 8018 wxRichTextAttr attr;
5d7836c4
JS
8019 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
8020 attr.SetRightIndent(rightIndent);
7fe8059f 8021
5d7836c4
JS
8022 return BeginStyle(attr);
8023}
8024
8025/// Begin paragraph spacing
8026bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
8027{
8028 long flags = 0;
8029 if (before != 0)
8030 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
8031 if (after != 0)
8032 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
8033
24777478 8034 wxRichTextAttr attr;
5d7836c4
JS
8035 attr.SetFlags(flags);
8036 attr.SetParagraphSpacingBefore(before);
8037 attr.SetParagraphSpacingAfter(after);
7fe8059f 8038
5d7836c4
JS
8039 return BeginStyle(attr);
8040}
8041
8042/// Begin line spacing
8043bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8044{
24777478 8045 wxRichTextAttr attr;
5d7836c4
JS
8046 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8047 attr.SetLineSpacing(lineSpacing);
7fe8059f 8048
5d7836c4
JS
8049 return BeginStyle(attr);
8050}
8051
8052/// Begin numbered bullet
8053bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8054{
24777478 8055 wxRichTextAttr attr;
f089713f 8056 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8057 attr.SetBulletStyle(bulletStyle);
8058 attr.SetBulletNumber(bulletNumber);
8059 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8060
5d7836c4
JS
8061 return BeginStyle(attr);
8062}
8063
8064/// Begin symbol bullet
d2d0adc7 8065bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 8066{
24777478 8067 wxRichTextAttr attr;
f089713f 8068 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8069 attr.SetBulletStyle(bulletStyle);
8070 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 8071 attr.SetBulletText(symbol);
7fe8059f 8072
5d7836c4
JS
8073 return BeginStyle(attr);
8074}
8075
f089713f
JS
8076/// Begin standard bullet
8077bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8078{
24777478 8079 wxRichTextAttr attr;
f089713f
JS
8080 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8081 attr.SetBulletStyle(bulletStyle);
8082 attr.SetLeftIndent(leftIndent, leftSubIndent);
8083 attr.SetBulletName(bulletName);
8084
8085 return BeginStyle(attr);
8086}
8087
5d7836c4
JS
8088/// Begin named character style
8089bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8090{
8091 if (GetStyleSheet())
8092 {
8093 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8094 if (def)
8095 {
24777478 8096 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8097 return BeginStyle(attr);
8098 }
8099 }
8100 return false;
8101}
8102
8103/// Begin named paragraph style
8104bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8105{
8106 if (GetStyleSheet())
8107 {
8108 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8109 if (def)
8110 {
24777478 8111 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8112 return BeginStyle(attr);
8113 }
8114 }
8115 return false;
8116}
8117
f089713f
JS
8118/// Begin named list style
8119bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8120{
8121 if (GetStyleSheet())
8122 {
8123 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8124 if (def)
8125 {
24777478 8126 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
8127
8128 attr.SetBulletNumber(number);
8129
8130 return BeginStyle(attr);
8131 }
8132 }
8133 return false;
8134}
8135
d2d0adc7
JS
8136/// Begin URL
8137bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8138{
24777478 8139 wxRichTextAttr attr;
d2d0adc7
JS
8140
8141 if (!characterStyle.IsEmpty() && GetStyleSheet())
8142 {
8143 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8144 if (def)
8145 {
336d8ae9 8146 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
8147 }
8148 }
8149 attr.SetURL(url);
8150
8151 return BeginStyle(attr);
8152}
8153
5d7836c4
JS
8154/// Adds a handler to the end
8155void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8156{
8157 sm_handlers.Append(handler);
8158}
8159
8160/// Inserts a handler at the front
8161void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8162{
8163 sm_handlers.Insert( handler );
8164}
8165
8166/// Removes a handler
8167bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8168{
8169 wxRichTextFileHandler *handler = FindHandler(name);
8170 if (handler)
8171 {
8172 sm_handlers.DeleteObject(handler);
8173 delete handler;
8174 return true;
8175 }
8176 else
8177 return false;
8178}
8179
8180/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
8181wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8182 wxRichTextFileType imageType)
5d7836c4
JS
8183{
8184 if (imageType != wxRICHTEXT_TYPE_ANY)
8185 return FindHandler(imageType);
0ca07313 8186 else if (!filename.IsEmpty())
5d7836c4
JS
8187 {
8188 wxString path, file, ext;
a51e601e 8189 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
8190 return FindHandler(ext, imageType);
8191 }
0ca07313
JS
8192 else
8193 return NULL;
5d7836c4
JS
8194}
8195
8196
8197/// Finds a handler by name
8198wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8199{
8200 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8201 while (node)
8202 {
8203 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8204 if (handler->GetName().Lower() == name.Lower()) return handler;
8205
8206 node = node->GetNext();
8207 }
8208 return NULL;
8209}
8210
8211/// Finds a handler by extension and type
d75a69e8 8212wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
8213{
8214 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8215 while (node)
8216 {
8217 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8218 if ( handler->GetExtension().Lower() == extension.Lower() &&
8219 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8220 return handler;
8221 node = node->GetNext();
8222 }
8223 return 0;
8224}
8225
8226/// Finds a handler by type
d75a69e8 8227wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
8228{
8229 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8230 while (node)
8231 {
8232 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8233 if (handler->GetType() == type) return handler;
8234 node = node->GetNext();
8235 }
8236 return NULL;
8237}
8238
8239void wxRichTextBuffer::InitStandardHandlers()
8240{
8241 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8242 AddHandler(new wxRichTextPlainTextHandler);
8243}
8244
8245void wxRichTextBuffer::CleanUpHandlers()
8246{
8247 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8248 while (node)
8249 {
8250 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8251 wxList::compatibility_iterator next = node->GetNext();
8252 delete handler;
8253 node = next;
8254 }
8255
8256 sm_handlers.Clear();
8257}
8258
1e967276 8259wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 8260{
1e967276
JS
8261 if (types)
8262 types->Clear();
8263
5d7836c4
JS
8264 wxString wildcard;
8265
8266 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8267 int count = 0;
8268 while (node)
8269 {
8270 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 8271 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
8272 {
8273 if (combine)
8274 {
8275 if (count > 0)
8276 wildcard += wxT(";");
8277 wildcard += wxT("*.") + handler->GetExtension();
8278 }
8279 else
8280 {
8281 if (count > 0)
8282 wildcard += wxT("|");
8283 wildcard += handler->GetName();
8284 wildcard += wxT(" ");
8285 wildcard += _("files");
8286 wildcard += wxT(" (*.");
8287 wildcard += handler->GetExtension();
8288 wildcard += wxT(")|*.");
8289 wildcard += handler->GetExtension();
1e967276
JS
8290 if (types)
8291 types->Add(handler->GetType());
5d7836c4
JS
8292 }
8293 count ++;
8294 }
8295
8296 node = node->GetNext();
8297 }
8298
8299 if (combine)
8300 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8301 return wildcard;
8302}
8303
8304/// Load a file
d75a69e8 8305bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8306{
8307 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8308 if (handler)
1e967276 8309 {
24777478 8310 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8311 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8312 bool success = handler->LoadFile(this, filename);
8313 Invalidate(wxRICHTEXT_ALL);
8314 return success;
8315 }
5d7836c4
JS
8316 else
8317 return false;
8318}
8319
8320/// Save a file
d75a69e8 8321bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8322{
8323 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8324 if (handler)
d2d0adc7
JS
8325 {
8326 handler->SetFlags(GetHandlerFlags());
5d7836c4 8327 return handler->SaveFile(this, filename);
d2d0adc7 8328 }
5d7836c4
JS
8329 else
8330 return false;
8331}
8332
8333/// Load from a stream
d75a69e8 8334bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8335{
8336 wxRichTextFileHandler* handler = FindHandler(type);
8337 if (handler)
1e967276 8338 {
24777478 8339 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8340 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8341 bool success = handler->LoadFile(this, stream);
8342 Invalidate(wxRICHTEXT_ALL);
8343 return success;
8344 }
5d7836c4
JS
8345 else
8346 return false;
8347}
8348
8349/// Save to a stream
d75a69e8 8350bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8351{
8352 wxRichTextFileHandler* handler = FindHandler(type);
8353 if (handler)
d2d0adc7
JS
8354 {
8355 handler->SetFlags(GetHandlerFlags());
5d7836c4 8356 return handler->SaveFile(this, stream);
d2d0adc7 8357 }
5d7836c4
JS
8358 else
8359 return false;
8360}
8361
8362/// Copy the range to the clipboard
8363bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8364{
8365 bool success = false;
603f702b
JS
8366 wxRichTextParagraphLayoutBox* container = this;
8367 if (GetRichTextCtrl())
8368 container = GetRichTextCtrl()->GetFocusObject();
8369
11ef729d 8370#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 8371
d2142335 8372 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 8373 {
0ca07313
JS
8374 wxTheClipboard->Clear();
8375
8376 // Add composite object
8377
8378 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8379
8380 {
603f702b 8381 wxString text = container->GetTextForRange(range);
0ca07313
JS
8382
8383#ifdef __WXMSW__
8384 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8385#endif
8386
8387 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8388 }
8389
8390 // Add rich text buffer data object. This needs the XML handler to be present.
8391
8392 if (FindHandler(wxRICHTEXT_TYPE_XML))
8393 {
8394 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 8395 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
8396
8397 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8398 }
8399
8400 if (wxTheClipboard->SetData(compositeObject))
8401 success = true;
8402
5d7836c4
JS
8403 wxTheClipboard->Close();
8404 }
0ca07313 8405
39a1c2f2
WS
8406#else
8407 wxUnusedVar(range);
8408#endif
5d7836c4
JS
8409 return success;
8410}
8411
8412/// Paste the clipboard content to the buffer
8413bool wxRichTextBuffer::PasteFromClipboard(long position)
8414{
8415 bool success = false;
603f702b
JS
8416 wxRichTextParagraphLayoutBox* container = this;
8417 if (GetRichTextCtrl())
8418 container = GetRichTextCtrl()->GetFocusObject();
8419
11ef729d 8420#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
8421 if (CanPasteFromClipboard())
8422 {
8423 if (wxTheClipboard->Open())
8424 {
0ca07313
JS
8425 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8426 {
8427 wxRichTextBufferDataObject data;
8428 wxTheClipboard->GetData(data);
8429 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8430 if (richTextBuffer)
8431 {
4e63bfb9 8432 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 8433 if (GetRichTextCtrl())
603f702b 8434 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
8435 delete richTextBuffer;
8436 }
8437 }
f7d83f24 8438 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
8439 #if wxUSE_UNICODE
8440 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8441 #endif
f7d83f24 8442 )
5d7836c4
JS
8443 {
8444 wxTextDataObject data;
8445 wxTheClipboard->GetData(data);
8446 wxString text(data.GetText());
c21f3211
JS
8447#ifdef __WXMSW__
8448 wxString text2;
8449 text2.Alloc(text.Length()+1);
8450 size_t i;
8451 for (i = 0; i < text.Length(); i++)
8452 {
8453 wxChar ch = text[i];
8454 if (ch != wxT('\r'))
8455 text2 += ch;
8456 }
8457#else
8458 wxString text2 = text;
8459#endif
4e63bfb9 8460 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 8461
62381daa
JS
8462 if (GetRichTextCtrl())
8463 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8464
5d7836c4
JS
8465 success = true;
8466 }
8467 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8468 {
8469 wxBitmapDataObject data;
8470 wxTheClipboard->GetData(data);
8471 wxBitmap bitmap(data.GetBitmap());
8472 wxImage image(bitmap.ConvertToImage());
8473
603f702b 8474 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 8475
5d7836c4
JS
8476 action->GetNewParagraphs().AddImage(image);
8477
8478 if (action->GetNewParagraphs().GetChildCount() == 1)
8479 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 8480
9c8e10ad 8481 action->SetPosition(position+1);
7fe8059f 8482
5d7836c4 8483 // Set the range we'll need to delete in Undo
9c8e10ad 8484 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 8485
5d7836c4
JS
8486 SubmitAction(action);
8487
8488 success = true;
8489 }
8490 wxTheClipboard->Close();
8491 }
8492 }
39a1c2f2
WS
8493#else
8494 wxUnusedVar(position);
8495#endif
5d7836c4
JS
8496 return success;
8497}
8498
8499/// Can we paste from the clipboard?
8500bool wxRichTextBuffer::CanPasteFromClipboard() const
8501{
7fe8059f 8502 bool canPaste = false;
11ef729d 8503#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 8504 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 8505 {
f7d83f24
VZ
8506 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8507#if wxUSE_UNICODE
603f702b
JS
8508 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8509#endif
8510 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8511 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 8512 {
7fe8059f 8513 canPaste = true;
5d7836c4
JS
8514 }
8515 wxTheClipboard->Close();
8516 }
39a1c2f2 8517#endif
5d7836c4
JS
8518 return canPaste;
8519}
8520
8521/// Dumps contents of buffer for debugging purposes
8522void wxRichTextBuffer::Dump()
8523{
8524 wxString text;
8525 {
8526 wxStringOutputStream stream(& text);
8527 wxTextOutputStream textStream(stream);
8528 Dump(textStream);
8529 }
8530
8531 wxLogDebug(text);
8532}
8533
d2d0adc7
JS
8534/// Add an event handler
8535bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8536{
8537 m_eventHandlers.Append(handler);
8538 return true;
8539}
8540
8541/// Remove an event handler
8542bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8543{
8544 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8545 if (node)
8546 {
8547 m_eventHandlers.Erase(node);
8548 if (deleteHandler)
8549 delete handler;
3e541562 8550
d2d0adc7
JS
8551 return true;
8552 }
8553 else
8554 return false;
8555}
8556
8557/// Clear event handlers
8558void wxRichTextBuffer::ClearEventHandlers()
8559{
8560 m_eventHandlers.Clear();
8561}
8562
8563/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8564/// otherwise will stop at the first successful one.
8565bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8566{
8567 bool success = false;
8568 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8569 {
8570 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8571 if (handler->ProcessEvent(event))
8572 {
8573 success = true;
8574 if (!sendToAll)
8575 return true;
8576 }
8577 }
8578 return success;
8579}
8580
8581/// Set style sheet and notify of the change
8582bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8583{
8584 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 8585
616c7cbd 8586 wxWindowID winid = wxID_ANY;
d2d0adc7 8587 if (GetRichTextCtrl())
616c7cbd 8588 winid = GetRichTextCtrl()->GetId();
3e541562 8589
616c7cbd 8590 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 8591 event.SetEventObject(GetRichTextCtrl());
603f702b 8592 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
8593 event.SetOldStyleSheet(oldSheet);
8594 event.SetNewStyleSheet(sheet);
8595 event.Allow();
3e541562 8596
d2d0adc7
JS
8597 if (SendEvent(event) && !event.IsAllowed())
8598 {
8599 if (sheet != oldSheet)
8600 delete sheet;
8601
8602 return false;
8603 }
8604
8605 if (oldSheet && oldSheet != sheet)
8606 delete oldSheet;
8607
8608 SetStyleSheet(sheet);
8609
8610 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8611 event.SetOldStyleSheet(NULL);
8612 event.Allow();
8613
8614 return SendEvent(event);
8615}
8616
8617/// Set renderer, deleting old one
8618void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8619{
8620 if (sm_renderer)
8621 delete sm_renderer;
8622 sm_renderer = renderer;
8623}
8624
603f702b
JS
8625/// Hit-testing: returns a flag indicating hit test details, plus
8626/// information about position
8db2e3ef 8627int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 8628{
8db2e3ef 8629 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
8630 if (ret != wxRICHTEXT_HITTEST_NONE)
8631 {
8632 return ret;
8633 }
8634 else
8635 {
8636 textPosition = m_ownRange.GetEnd()-1;
8637 *obj = this;
8638 *contextObj = this;
8639 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8640 }
8641}
8642
32423dd8
JS
8643void wxRichTextBuffer::SetFontScale(double fontScale)
8644{
8645 m_fontScale = fontScale;
8646 m_fontTable.SetFontScale(fontScale);
8647}
8648
8649void wxRichTextBuffer::SetDimensionScale(double dimScale)
8650{
8651 m_dimensionScale = dimScale;
8652}
8653
24777478 8654bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8655{
a1b806b9 8656 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8657 {
ecb5fbf1
JS
8658 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8659 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8660 }
8661 else
8662 {
ecb5fbf1
JS
8663 wxCheckSetPen(dc, *wxBLACK_PEN);
8664 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8665 }
8666
8667 wxFont font;
44cc96a8
JS
8668 if (bulletAttr.HasFont())
8669 {
8670 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8671 }
d2d0adc7
JS
8672 else
8673 font = (*wxNORMAL_FONT);
8674
ecb5fbf1 8675 wxCheckSetFont(dc, font);
d2d0adc7
JS
8676
8677 int charHeight = dc.GetCharHeight();
3e541562 8678
d2d0adc7
JS
8679 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8680 int bulletHeight = bulletWidth;
8681
8682 int x = rect.x;
3e541562 8683
d2d0adc7
JS
8684 // Calculate the top position of the character (as opposed to the whole line height)
8685 int y = rect.y + (rect.height - charHeight);
3e541562 8686
d2d0adc7
JS
8687 // Calculate where the bullet should be positioned
8688 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8689
d2d0adc7 8690 // The margin between a bullet and text.
44219ff0 8691 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8692
d2d0adc7
JS
8693 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8694 x = rect.x + rect.width - bulletWidth - margin;
8695 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8696 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8697
d2d0adc7
JS
8698 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8699 {
8700 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8701 }
8702 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8703 {
8704 wxPoint pts[5];
8705 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8706 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8707 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8708 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8709
d2d0adc7
JS
8710 dc.DrawPolygon(4, pts);
8711 }
8712 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8713 {
8714 wxPoint pts[3];
8715 pts[0].x = x; pts[0].y = y;
8716 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8717 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8718
d2d0adc7
JS
8719 dc.DrawPolygon(3, pts);
8720 }
603f702b
JS
8721 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8722 {
8723 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8724 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8725 }
d2d0adc7
JS
8726 else // "standard/circle", and catch-all
8727 {
8728 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8729 }
8730
d2d0adc7
JS
8731 return true;
8732}
8733
24777478 8734bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8735{
8736 if (!text.empty())
8737 {
8738 wxFont font;
44cc96a8
JS
8739 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8740 {
24777478 8741 wxRichTextAttr fontAttr;
32423dd8
JS
8742 if (attr.HasFontPixelSize())
8743 fontAttr.SetFontPixelSize(attr.GetFontSize());
8744 else
8745 fontAttr.SetFontPointSize(attr.GetFontSize());
44cc96a8
JS
8746 fontAttr.SetFontStyle(attr.GetFontStyle());
8747 fontAttr.SetFontWeight(attr.GetFontWeight());
8748 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8749 fontAttr.SetFontFaceName(attr.GetBulletFont());
8750 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8751 }
8752 else if (attr.HasFont())
8753 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8754 else
8755 font = (*wxNORMAL_FONT);
8756
ecb5fbf1 8757 wxCheckSetFont(dc, font);
d2d0adc7 8758
a1b806b9 8759 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8760 dc.SetTextForeground(attr.GetTextColour());
8761
04ee05f9 8762 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8763
8764 int charHeight = dc.GetCharHeight();
8765 wxCoord tw, th;
8766 dc.GetTextExtent(text, & tw, & th);
8767
8768 int x = rect.x;
8769
8770 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8771 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8772
8773 // The margin between a bullet and text.
44219ff0 8774 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8775
d2d0adc7
JS
8776 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8777 x = (rect.x + rect.width) - tw - margin;
8778 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8779 x = x + (rect.width)/2 - tw/2;
8780
8781 dc.DrawText(text, x, y);
3e541562 8782
d2d0adc7
JS
8783 return true;
8784 }
8785 else
8786 return false;
8787}
8788
24777478 8789bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8790{
8791 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8792 // with the buffer. The store will allow retrieval from memory, disk or other means.
8793 return false;
8794}
8795
8796/// Enumerate the standard bullet names currently supported
8797bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8798{
04529b2a 8799 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8800 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8801 bulletNames.Add(wxTRANSLATE("standard/square"));
8802 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8803 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8804
8805 return true;
8806}
5d7836c4 8807
bec80f4f
JS
8808/*!
8809 * wxRichTextBox
8810 */
8811
603f702b 8812IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8813
8814wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8815 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8816{
8817}
8818
8819/// Draw the item
8db2e3ef 8820bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8821{
603f702b
JS
8822 if (!IsShown())
8823 return true;
5ad9ae3a 8824
603f702b
JS
8825 // TODO: if the active object in the control, draw an indication.
8826 // We need to add the concept of active object, and not just focus object,
8827 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8828 // Ultimately we would like to be able to interactively resize an active object
8829 // using drag handles.
8db2e3ef 8830 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8831}
5ad9ae3a 8832
603f702b
JS
8833/// Copy
8834void wxRichTextBox::Copy(const wxRichTextBox& obj)
8835{
8836 wxRichTextParagraphLayoutBox::Copy(obj);
8837}
8838
8839// Edit properties via a GUI
8840bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8841{
8842 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8843 boxDlg.SetAttributes(GetAttributes());
8844
8845 if (boxDlg.ShowModal() == wxID_OK)
8846 {
8847 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8848 // indeterminate in the object.
8849 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8850 return true;
5ad9ae3a 8851 }
603f702b
JS
8852 else
8853 return false;
bec80f4f
JS
8854}
8855
7c9fdebe
JS
8856/*!
8857 * wxRichTextField
8858 */
8859
8860IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8861
8862wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8863 wxRichTextParagraphLayoutBox(parent)
8864{
8865 SetFieldType(fieldType);
8866}
8867
8868/// Draw the item
8869bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8870{
8871 if (!IsShown())
8872 return true;
8873
8874 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8875 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8876 return true;
8877
8878 // Fallback; but don't draw guidelines.
8879 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8880 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8881}
8882
8883bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8884{
8885 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8886 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8887 return true;
8888
8889 // Fallback
8890 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8891}
8892
914a4e23 8893bool 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
8894{
8895 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8896 if (fieldType)
914a4e23 8897 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
7c9fdebe 8898
914a4e23 8899 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
7c9fdebe
JS
8900}
8901
8902/// Calculate range
8903void wxRichTextField::CalculateRange(long start, long& end)
8904{
8905 if (IsTopLevel())
8906 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8907 else
8908 wxRichTextObject::CalculateRange(start, end);
8909}
8910
8911/// Copy
8912void wxRichTextField::Copy(const wxRichTextField& obj)
8913{
8914 wxRichTextParagraphLayoutBox::Copy(obj);
8915
32423dd8 8916 UpdateField(GetBuffer());
7c9fdebe
JS
8917}
8918
8919// Edit properties via a GUI
8920bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8921{
8922 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8923 if (fieldType)
8924 return fieldType->EditProperties(this, parent, buffer);
8925
8926 return false;
8927}
8928
8929bool wxRichTextField::CanEditProperties() const
8930{
8931 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8932 if (fieldType)
8933 return fieldType->CanEditProperties((wxRichTextField*) this);
8934
8935 return false;
8936}
8937
8938wxString wxRichTextField::GetPropertiesMenuLabel() const
8939{
8940 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8941 if (fieldType)
8942 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8943
8944 return wxEmptyString;
8945}
8946
32423dd8 8947bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
7c9fdebe
JS
8948{
8949 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8950 if (fieldType)
32423dd8 8951 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
7c9fdebe
JS
8952
8953 return false;
8954}
8955
8956bool wxRichTextField::IsTopLevel() const
8957{
8958 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8959 if (fieldType)
8960 return fieldType->IsTopLevel((wxRichTextField*) this);
8961
8962 return true;
8963}
8964
8965IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8966
8967IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8968
8969wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8970{
8971 Init();
8972
8973 SetName(name);
8974 SetLabel(label);
8975 SetDisplayStyle(displayStyle);
8976}
8977
8978wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8979{
8980 Init();
8981
8982 SetName(name);
8983 SetBitmap(bitmap);
8984 SetDisplayStyle(displayStyle);
8985}
8986
8987void wxRichTextFieldTypeStandard::Init()
8988{
8989 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8990 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8991 m_textColour = *wxWHITE;
8992 m_borderColour = *wxBLACK;
8993 m_backgroundColour = *wxBLACK;
8994 m_verticalPadding = 1;
8995 m_horizontalPadding = 3;
8996 m_horizontalMargin = 2;
8997 m_verticalMargin = 0;
8998}
8999
9000void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
9001{
9002 wxRichTextFieldType::Copy(field);
9003
9004 m_label = field.m_label;
9005 m_displayStyle = field.m_displayStyle;
9006 m_font = field.m_font;
9007 m_textColour = field.m_textColour;
9008 m_borderColour = field.m_borderColour;
9009 m_backgroundColour = field.m_backgroundColour;
9010 m_verticalPadding = field.m_verticalPadding;
9011 m_horizontalPadding = field.m_horizontalPadding;
9012 m_horizontalMargin = field.m_horizontalMargin;
9013 m_bitmap = field.m_bitmap;
9014}
9015
9016bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
9017{
9018 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9019 return false; // USe default composite drawing
9020 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9021 {
9022 int borderSize = 1;
9023
9024 wxPen borderPen(m_borderColour, 1, wxSOLID);
9025 wxBrush backgroundBrush(m_backgroundColour);
9026 wxColour textColour(m_textColour);
9027
9028 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9029 {
9030 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
9031 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
9032
9033 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
9034 backgroundBrush = wxBrush(highlightColour);
9035
9036 wxCheckSetBrush(dc, backgroundBrush);
9037 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9038 dc.DrawRectangle(rect);
9039 }
9040
9041 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9042 borderSize = 0;
9043
9044 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9045 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9046 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9047
9048 // clientArea is where the text is actually written
9049 wxRect clientArea = objectRect;
9050
9051 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9052 {
9053 dc.SetPen(borderPen);
9054 dc.SetBrush(backgroundBrush);
9055 dc.DrawRoundedRectangle(objectRect, 4.0);
9056 }
9057 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9058 {
9059 int arrowLength = objectRect.height/2;
9060 clientArea.width -= (arrowLength - m_horizontalPadding);
9061
9062 wxPoint pts[5];
9063 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9064 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9065 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9066 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9067 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9068 dc.SetPen(borderPen);
9069 dc.SetBrush(backgroundBrush);
9070 dc.DrawPolygon(5, pts);
9071 }
9072 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9073 {
9074 int arrowLength = objectRect.height/2;
9075 clientArea.width -= (arrowLength - m_horizontalPadding);
9076 clientArea.x += (arrowLength - m_horizontalPadding);
9077
9078 wxPoint pts[5];
9079 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9080 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9081 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9082 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9083 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9084 dc.SetPen(borderPen);
9085 dc.SetBrush(backgroundBrush);
9086 dc.DrawPolygon(5, pts);
9087 }
9088
9089 if (m_bitmap.IsOk())
9090 {
9091 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9092 int y = clientArea.y + m_verticalPadding;
9093 dc.DrawBitmap(m_bitmap, x, y, true);
9094
9095 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9096 {
9097 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9098 wxCheckSetPen(dc, *wxBLACK_PEN);
9099 dc.SetLogicalFunction(wxINVERT);
9100 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9101 dc.SetLogicalFunction(wxCOPY);
9102 }
9103 }
9104 else
9105 {
9106 wxString label(m_label);
9107 if (label.IsEmpty())
9108 label = wxT("??");
9109 int w, h, maxDescent;
9110 dc.SetFont(m_font);
9111 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9112 dc.SetTextForeground(textColour);
9113
9114 int x = clientArea.x + (clientArea.width - w)/2;
9115 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9116 dc.DrawText(m_label, x, y);
9117 }
9118 }
9119
9120 return true;
9121}
9122
9123bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9124{
9125 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9126 return false; // USe default composite layout
9127
9128 wxSize size = GetSize(obj, dc, context, style);
9129 obj->SetCachedSize(size);
9130 obj->SetMinSize(size);
9131 obj->SetMaxSize(size);
9132 return true;
9133}
9134
914a4e23 9135bool 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
9136{
9137 if (IsTopLevel(obj))
914a4e23 9138 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
7c9fdebe
JS
9139 else
9140 {
9141 wxSize sz = GetSize(obj, dc, context, 0);
9142 if (partialExtents)
9143 {
9144 int lastSize;
9145 if (partialExtents->GetCount() > 0)
9146 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9147 else
9148 lastSize = 0;
9149 partialExtents->Add(lastSize + sz.x);
9150 }
9151 size = sz;
9152 return true;
9153 }
9154}
9155
9156wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9157{
9158 int borderSize = 1;
9159 int w = 0, h = 0, maxDescent = 0;
9160
9161 wxSize sz;
9162 if (m_bitmap.IsOk())
9163 {
9164 w = m_bitmap.GetWidth();
9165 h = m_bitmap.GetHeight();
9166
9167 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9168 }
9169 else
9170 {
9171 wxString label(m_label);
9172 if (label.IsEmpty())
9173 label = wxT("??");
9174 dc.SetFont(m_font);
9175 dc.GetTextExtent(label, & w, &h, & maxDescent);
9176
9177 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9178 }
9179
9180 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9181 {
9182 sz.x += borderSize*2;
9183 sz.y += borderSize*2;
9184 }
9185
9186 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9187 {
9188 // Add space for the arrow
9189 sz.x += (sz.y/2 - m_horizontalPadding);
9190 }
9191
9192 return sz;
9193}
9194
603f702b
JS
9195IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9196
9197wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9198 wxRichTextBox(parent)
bec80f4f 9199{
603f702b
JS
9200}
9201
9202/// Draw the item
8db2e3ef 9203bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9204{
8db2e3ef 9205 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
9206}
9207
9208/// Copy
9209void wxRichTextCell::Copy(const wxRichTextCell& obj)
9210{
9211 wxRichTextBox::Copy(obj);
9212}
9213
9214// Edit properties via a GUI
9215bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9216{
9217 // We need to gather common attributes for all selected cells.
9218
9219 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9220 bool multipleCells = false;
9221 wxRichTextAttr attr;
9222
9223 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9224 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 9225 {
603f702b
JS
9226 wxRichTextAttr clashingAttr, absentAttr;
9227 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9228 size_t i;
9229 int selectedCellCount = 0;
9230 for (i = 0; i < sel.GetCount(); i++)
9231 {
9232 const wxRichTextRange& range = sel[i];
9233 wxRichTextCell* cell = table->GetCell(range.GetStart());
9234 if (cell)
9235 {
9236 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 9237
603f702b
JS
9238 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9239
9240 selectedCellCount ++;
9241 }
9242 }
9243 multipleCells = selectedCellCount > 1;
5ad9ae3a 9244 }
603f702b
JS
9245 else
9246 {
9247 attr = GetAttributes();
9248 }
9249
9250 wxString caption;
9251 if (multipleCells)
9252 caption = _("Multiple Cell Properties");
9253 else
9254 caption = _("Cell Properties");
9255
9256 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9257 cellDlg.SetAttributes(attr);
9258
80a46597 9259 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
603f702b
JS
9260 if (sizePage)
9261 {
9262 // We don't want position and floating controls for a cell.
9263 sizePage->ShowPositionControls(false);
9264 sizePage->ShowFloatingControls(false);
9265 }
9266
9267 if (cellDlg.ShowModal() == wxID_OK)
9268 {
9269 if (multipleCells)
9270 {
9271 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9272 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9273 // since it may represent clashing attributes across multiple objects.
9274 table->SetCellStyle(sel, attr);
9275 }
9276 else
9277 // For a single object, indeterminate attributes set by the user should be reflected in the
9278 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9279 // the style directly instead of applying (which ignores indeterminate attributes,
9280 // leaving them as they were).
9281 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9282 return true;
9283 }
9284 else
9285 return false;
9286}
9287
9288WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9289
9290IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9291
9292wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9293{
9294 m_rowCount = 0;
9295 m_colCount = 0;
9296}
5ad9ae3a 9297
603f702b 9298// Draws the object.
8db2e3ef 9299bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9300{
8db2e3ef 9301 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
9302}
9303
603f702b
JS
9304WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9305WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9306
9307// Lays the object out. rect is the space available for layout. Often it will
9308// be the specified overall space for this object, if trying to constrain
9309// layout to a particular size, or it could be the total space available in the
9310// parent. rect is the overall size, so we must subtract margins and padding.
9311// to get the actual available space.
8db2e3ef 9312bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 9313{
603f702b
JS
9314 SetPosition(rect.GetPosition());
9315
9316 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9317 // minimum size if within alloted size, then divide up remaining size
9318 // between rows/cols.
9319
9320 double scale = 1.0;
9321 wxRichTextBuffer* buffer = GetBuffer();
9322 if (buffer) scale = buffer->GetScale();
9323
8db2e3ef 9324 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
9325 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9326
8db2e3ef
JS
9327 wxRichTextAttr attr(GetAttributes());
9328 context.ApplyVirtualAttributes(attr, this);
9329
603f702b
JS
9330 // If we have no fixed table size, and assuming we're not pushed for
9331 // space, then we don't have to try to stretch the table to fit the contents.
9332 bool stretchToFitTableWidth = false;
9333
9334 int tableWidth = rect.width;
8db2e3ef 9335 if (attr.GetTextBoxAttr().GetWidth().IsValid())
603f702b 9336 {
8db2e3ef 9337 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
9338
9339 // Fixed table width, so we do want to stretch columns out if necessary.
9340 stretchToFitTableWidth = true;
9341
9342 // Shouldn't be able to exceed the size passed to this function
9343 tableWidth = wxMin(rect.width, tableWidth);
9344 }
9345
9346 // Get internal padding
36307fdf 9347 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
9348 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9349 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9350 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9351 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
9352
9353 // Assume that left and top padding are also used for inter-cell padding.
9354 int paddingX = paddingLeft;
9355 int paddingY = paddingTop;
9356
9357 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 9358 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
9359
9360 // Internal table width - the area for content
9361 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9362
9363 int rowCount = m_cells.GetCount();
9364 if (m_colCount == 0 || rowCount == 0)
9365 {
9366 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9367 SetCachedSize(overallRect.GetSize());
9368
9369 // Zero content size
9370 SetMinSize(overallRect.GetSize());
9371 SetMaxSize(GetMinSize());
9372 return true;
9373 }
9374
9375 // The final calculated widths
bb7bbd12
JS
9376 wxArrayInt colWidths;
9377 colWidths.Add(0, m_colCount);
603f702b 9378
bb7bbd12
JS
9379 wxArrayInt absoluteColWidths;
9380 absoluteColWidths.Add(0, m_colCount);
7c9fdebe 9381
bb7bbd12
JS
9382 wxArrayInt percentageColWidths;
9383 percentageColWidths.Add(0, m_colCount);
603f702b
JS
9384 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9385 // These are only relevant when the first column contains spanning information.
9386 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
9387 wxArrayInt maxColWidths;
9388 maxColWidths.Add(0, m_colCount);
9389 wxArrayInt minColWidths;
9390 minColWidths.Add(0, m_colCount);
603f702b
JS
9391
9392 wxSize tableSize(tableWidth, 0);
9393
9394 int i, j, k;
9395
9396 for (i = 0; i < m_colCount; i++)
9397 {
9398 absoluteColWidths[i] = 0;
9399 // absoluteColWidthsSpanning[i] = 0;
9400 percentageColWidths[i] = -1;
9401 // percentageColWidthsSpanning[i] = -1;
9402 colWidths[i] = 0;
9403 maxColWidths[i] = 0;
9404 minColWidths[i] = 0;
9405 // columnSpans[i] = 1;
9406 }
9407
9408 // (0) Determine which cells are visible according to spans
9409 // 1 2 3 4 5
9410 // __________________
9411 // | | | | | 1
9412 // |------| |----|
9413 // |------| | | 2
9414 // |------| | | 3
9415 // |------------------|
9416 // |__________________| 4
9417
9418 // To calculate cell visibility:
9419 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9420 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9421 // that cell, hide the cell.
9422
9423 // We can also use this array to match the size of spanning cells to the grid. Or just do
9424 // this when we iterate through all cells.
9425
9426 // 0.1: add spanning cells to an array
9427 wxRichTextRectArray rectArray;
9428 for (j = 0; j < m_rowCount; j++)
9429 {
9430 for (i = 0; i < m_colCount; i++)
9431 {
9432 wxRichTextBox* cell = GetCell(j, i);
9433 int colSpan = 1, rowSpan = 1;
9434 if (cell->GetProperties().HasProperty(wxT("colspan")))
9435 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9436 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9437 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9438 if (colSpan > 1 || rowSpan > 1)
9439 {
9440 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9441 }
9442 }
9443 }
9444 // 0.2: find which cells are subsumed by a spanning cell
9445 for (j = 0; j < m_rowCount; j++)
9446 {
9447 for (i = 0; i < m_colCount; i++)
9448 {
9449 wxRichTextBox* cell = GetCell(j, i);
9450 if (rectArray.GetCount() == 0)
9451 {
9452 cell->Show(true);
9453 }
9454 else
9455 {
9456 int colSpan = 1, rowSpan = 1;
9457 if (cell->GetProperties().HasProperty(wxT("colspan")))
9458 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9459 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9460 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9461 if (colSpan > 1 || rowSpan > 1)
9462 {
9463 // Assume all spanning cells are shown
9464 cell->Show(true);
9465 }
9466 else
9467 {
9468 bool shown = true;
9469 for (k = 0; k < (int) rectArray.GetCount(); k++)
9470 {
9471 if (rectArray[k].Contains(wxPoint(i, j)))
9472 {
9473 shown = false;
9474 break;
9475 }
9476 }
9477 cell->Show(shown);
9478 }
9479 }
9480 }
9481 }
9482
9483 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9484 // overlap with a spanned cell starting at a previous column position.
9485 // This means we need to keep an array of rects so we can check. However
9486 // it does also mean that some spans simply may not be taken into account
9487 // where there are different spans happening on different rows. In these cases,
9488 // they will simply be as wide as their constituent columns.
9489
9490 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9491 // the absolute or percentage width of each column.
9492
9493 for (j = 0; j < m_rowCount; j++)
9494 {
9495 // First get the overall margins so we can calculate percentage widths based on
9496 // the available content space for all cells on the row
9497
9498 int overallRowContentMargin = 0;
9499 int visibleCellCount = 0;
9500
9501 for (i = 0; i < m_colCount; i++)
9502 {
9503 wxRichTextBox* cell = GetCell(j, i);
9504 if (cell->IsShown())
9505 {
9506 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9507 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9508
9509 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9510 visibleCellCount ++;
9511 }
9512 }
9513
9514 // Add in inter-cell padding
9515 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9516
9517 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9518 wxSize rowTableSize(rowContentWidth, 0);
9519 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9520
9521 for (i = 0; i < m_colCount; i++)
9522 {
9523 wxRichTextBox* cell = GetCell(j, i);
9524 if (cell->IsShown())
9525 {
9526 int colSpan = 1;
9527 if (cell->GetProperties().HasProperty(wxT("colspan")))
9528 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9529
9530 // Lay out cell to find min/max widths
9531 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9532 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
9533
9534 if (colSpan == 1)
9535 {
9536 int absoluteCellWidth = -1;
9537 int percentageCellWidth = -1;
9538
9539 // I think we need to calculate percentages from the internal table size,
9540 // minus the padding between cells which we'll need to calculate from the
9541 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9542 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9543 // so if we want to conform to that we'll need to add in the overall cell margins.
9544 // However, this will make it difficult to specify percentages that add up to
9545 // 100% and still fit within the table width.
9546 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9547 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9548 // If we're using internal content size for the width, we would calculate the
9549 // the overall cell width for n cells as:
9550 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9551 // + thisOverallCellMargin
9552 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9553 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9554
9555 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9556 {
9557 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9558 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9559 {
9560 percentageCellWidth = w;
9561 }
9562 else
9563 {
9564 absoluteCellWidth = w;
9565 }
9566 // Override absolute width with minimum width if necessary
9567 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9568 absoluteCellWidth = cell->GetMinSize().x;
9569 }
9570
9571 if (absoluteCellWidth != -1)
9572 {
9573 if (absoluteCellWidth > absoluteColWidths[i])
9574 absoluteColWidths[i] = absoluteCellWidth;
9575 }
9576
9577 if (percentageCellWidth != -1)
9578 {
9579 if (percentageCellWidth > percentageColWidths[i])
9580 percentageColWidths[i] = percentageCellWidth;
9581 }
9582
9583 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9584 minColWidths[i] = cell->GetMinSize().x;
9585 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9586 maxColWidths[i] = cell->GetMaxSize().x;
9587 }
9588 }
9589 }
9590 }
9591
9592 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9593 // TODO: simply merge this into (1).
9594 for (i = 0; i < m_colCount; i++)
9595 {
9596 if (absoluteColWidths[i] > 0)
9597 {
9598 colWidths[i] = absoluteColWidths[i];
9599 }
9600 else if (percentageColWidths[i] > 0)
9601 {
9602 colWidths[i] = percentageColWidths[i];
9603
9604 // This is rubbish - we calculated the absolute widths from percentages, so
9605 // we can't do it again here.
9606 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9607 }
9608 }
9609
9610 // (3) Process absolute or proportional widths of spanning columns,
9611 // now that we know what our fixed column widths are going to be.
9612 // Spanned cells will try to adjust columns so the span will fit.
9613 // Even existing fixed column widths can be expanded if necessary.
9614 // Actually, currently fixed columns widths aren't adjusted; instead,
9615 // the algorithm favours earlier rows and adjusts unspecified column widths
9616 // the first time only. After that, we can't know whether the column has been
9617 // specified explicitly or not. (We could make a note if necessary.)
9618 for (j = 0; j < m_rowCount; j++)
9619 {
9620 // First get the overall margins so we can calculate percentage widths based on
9621 // the available content space for all cells on the row
9622
9623 int overallRowContentMargin = 0;
9624 int visibleCellCount = 0;
9625
9626 for (i = 0; i < m_colCount; i++)
9627 {
9628 wxRichTextBox* cell = GetCell(j, i);
9629 if (cell->IsShown())
9630 {
9631 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9632 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9633
9634 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9635 visibleCellCount ++;
9636 }
9637 }
9638
9639 // Add in inter-cell padding
9640 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9641
9642 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9643 wxSize rowTableSize(rowContentWidth, 0);
9644 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9645
9646 for (i = 0; i < m_colCount; i++)
9647 {
9648 wxRichTextBox* cell = GetCell(j, i);
9649 if (cell->IsShown())
9650 {
9651 int colSpan = 1;
9652 if (cell->GetProperties().HasProperty(wxT("colspan")))
9653 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9654
9655 if (colSpan > 1)
9656 {
9657 int spans = wxMin(colSpan, m_colCount - i);
9658 int cellWidth = 0;
9659 if (spans > 0)
9660 {
9661 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9662 {
9663 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9664 // Override absolute width with minimum width if necessary
9665 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9666 cellWidth = cell->GetMinSize().x;
9667 }
9668 else
9669 {
9670 // Do we want to do this? It's the only chance we get to
9671 // use the cell's min/max sizes, so we need to work out
9672 // how we're going to balance the unspecified spanning cell
9673 // width with the possibility more-constrained constituent cell widths.
9674 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9675 // don't want to constraint all the spanned columns to fit into this cell.
9676 // OK, let's say that if any of the constituent columns don't fit,
9677 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9678 // cells to the columns later.
9679 cellWidth = cell->GetMinSize().x;
9680 if (cell->GetMaxSize().x > cellWidth)
9681 cellWidth = cell->GetMaxSize().x;
9682 }
9683
9684 // Subtract the padding between cells
9685 int spanningWidth = cellWidth;
9686 spanningWidth -= paddingX * (spans-1);
9687
9688 if (spanningWidth > 0)
9689 {
9690 // Now share the spanning width between columns within that span
9691 // TODO: take into account min widths of columns within the span
9692 int spanningWidthLeft = spanningWidth;
9693 int stretchColCount = 0;
9694 for (k = i; k < (i+spans); k++)
9695 {
9696 if (colWidths[k] > 0) // absolute or proportional width has been specified
9697 spanningWidthLeft -= colWidths[k];
9698 else
9699 stretchColCount ++;
9700 }
9701 // Now divide what's left between the remaining columns
9702 int colShare = 0;
9703 if (stretchColCount > 0)
9704 colShare = spanningWidthLeft / stretchColCount;
9705 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9706
9707 // If fixed-width columns are currently too big, then we'll later
9708 // stretch the spanned cell to fit.
9709
9710 if (spanningWidthLeft > 0)
9711 {
9712 for (k = i; k < (i+spans); k++)
9713 {
9714 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9715 {
9716 int newWidth = colShare;
9717 if (k == (i+spans-1))
9718 newWidth += colShareRemainder; // ensure all pixels are filled
9719 colWidths[k] = newWidth;
9720 }
9721 }
9722 }
9723 }
9724 }
9725 }
9726 }
9727 }
9728 }
9729
9730 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9731 // TODO: take into account min widths of columns within the span
9732 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9733 int widthLeft = tableWidthMinusPadding;
9734 int stretchColCount = 0;
9735 for (i = 0; i < m_colCount; i++)
9736 {
9737 // TODO: we need to take into account min widths.
9738 // Subtract min width from width left, then
9739 // add the colShare to the min width
9740 if (colWidths[i] > 0) // absolute or proportional width has been specified
9741 widthLeft -= colWidths[i];
9742 else
9743 {
9744 if (minColWidths[i] > 0)
9745 widthLeft -= minColWidths[i];
9746
9747 stretchColCount ++;
9748 }
9749 }
9750
9751 // Now divide what's left between the remaining columns
9752 int colShare = 0;
9753 if (stretchColCount > 0)
9754 colShare = widthLeft / stretchColCount;
9755 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9756
9757 // Check we don't have enough space, in which case shrink all columns, overriding
9758 // any absolute/proportional widths
9759 // TODO: actually we would like to divide up the shrinkage according to size.
9760 // How do we calculate the proportions that will achieve this?
9761 // Could first choose an arbitrary value for stretching cells, and then calculate
9762 // factors to multiply each width by.
9763 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9764 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9765 {
9766 colShare = tableWidthMinusPadding / m_colCount;
9767 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9768 for (i = 0; i < m_colCount; i++)
9769 {
9770 colWidths[i] = 0;
9771 minColWidths[i] = 0;
9772 }
9773 }
9774
9775 // We have to adjust the columns if either we need to shrink the
9776 // table to fit the parent/table width, or we explicitly set the
9777 // table width and need to stretch out the table.
9778 if (widthLeft < 0 || stretchToFitTableWidth)
9779 {
9780 for (i = 0; i < m_colCount; i++)
9781 {
9782 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9783 {
9784 if (minColWidths[i] > 0)
9785 colWidths[i] = minColWidths[i] + colShare;
9786 else
9787 colWidths[i] = colShare;
9788 if (i == (m_colCount-1))
9789 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9790 }
9791 }
9792 }
9793
9794 // TODO: if spanned cells have no specified or max width, make them the
9795 // as big as the columns they span. Do this for all spanned cells in all
9796 // rows, of course. Size any spanned cells left over at the end - even if they
9797 // have width > 0, make sure they're limited to the appropriate column edge.
9798
9799
9800/*
9801 Sort out confusion between content width
9802 and overall width later. For now, assume we specify overall width.
9803
9804 So, now we've laid out the table to fit into the given space
9805 and have used specified widths and minimum widths.
9806
9807 Now we need to consider how we will try to take maximum width into account.
9808
9809*/
9810
9811 // (??) TODO: take max width into account
9812
9813 // (6) Lay out all cells again with the current values
9814
9815 int maxRight = 0;
9816 int y = availableSpace.y;
9817 for (j = 0; j < m_rowCount; j++)
9818 {
9819 int x = availableSpace.x; // TODO: take into account centering etc.
9820 int maxCellHeight = 0;
9821 int maxSpecifiedCellHeight = 0;
9822
bb7bbd12
JS
9823 wxArrayInt actualWidths;
9824 actualWidths.Add(0, m_colCount);
603f702b
JS
9825
9826 wxTextAttrDimensionConverter converter(dc, scale);
9827 for (i = 0; i < m_colCount; i++)
9828 {
9829 wxRichTextCell* cell = GetCell(j, i);
9830 if (cell->IsShown())
9831 {
603f702b
JS
9832 // Get max specified cell height
9833 // Don't handle percentages for height
9834 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9835 {
9836 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9837 if (h > maxSpecifiedCellHeight)
9838 maxSpecifiedCellHeight = h;
9839 }
9840
9841 if (colWidths[i] > 0) // absolute or proportional width has been specified
9842 {
9843 int colSpan = 1;
9844 if (cell->GetProperties().HasProperty(wxT("colspan")))
9845 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9846
9847 wxRect availableCellSpace;
9848
9849 // TODO: take into acount spans
9850 if (colSpan > 1)
9851 {
9852 // Calculate the size of this spanning cell from its constituent columns
9853 int xx = x;
9854 int spans = wxMin(colSpan, m_colCount - i);
9855 for (k = i; k < spans; k++)
9856 {
9857 if (k != i)
9858 xx += paddingX;
9859 xx += colWidths[k];
9860 }
9861 availableCellSpace = wxRect(x, y, xx, -1);
9862 }
9863 else
9864 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9865
9866 // Store actual width so we can force cell to be the appropriate width on the final loop
9867 actualWidths[i] = availableCellSpace.GetWidth();
9868
9869 // Lay out cell
9870 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9871 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9872
9873 // TODO: use GetCachedSize().x to compute 'natural' size
9874
9875 x += (availableCellSpace.GetWidth() + paddingX);
9876 if (cell->GetCachedSize().y > maxCellHeight)
9877 maxCellHeight = cell->GetCachedSize().y;
9878 }
9879 }
9880 }
9881
9882 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9883
9884 for (i = 0; i < m_colCount; i++)
9885 {
9886 wxRichTextCell* cell = GetCell(j, i);
9887 if (cell->IsShown())
9888 {
9889 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9890 // Lay out cell with new height
9891 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9892 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9893
9894 // Make sure the cell size really is the appropriate size,
9895 // not the calculated box size
9896 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9897
9898 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9899 }
9900 }
9901
9902 y += maxCellHeight;
9903 if (j < (m_rowCount-1))
9904 y += paddingY;
9905 }
9906
9907 // We need to add back the margins etc.
9908 {
9909 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9910 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 9911 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
9912 SetCachedSize(marginRect.GetSize());
9913 }
9914
9915 // TODO: calculate max size
9916 {
9917 SetMaxSize(GetCachedSize());
9918 }
9919
9920 // TODO: calculate min size
9921 {
9922 SetMinSize(GetCachedSize());
9923 }
9924
9925 // TODO: currently we use either a fixed table width or the parent's size.
9926 // We also want to be able to calculate the table width from its content,
9927 // whether using fixed column widths or cell content min/max width.
9928 // Probably need a boolean flag to say whether we need to stretch cells
9929 // to fit the table width, or to simply use min/max cell widths. The
9930 // trouble with this is that if cell widths are not specified, they
9931 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9932 // Anyway, ignoring that problem, we probably need to factor layout into a function
9933 // that can can calculate the maximum unconstrained layout in case table size is
9934 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9935 // constrain Layout(), or the previously-calculated max size to constraint layout.
9936
9937 return true;
9938}
9939
9940// Finds the absolute position and row height for the given character position
8db2e3ef 9941bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
9942{
9943 wxRichTextCell* child = GetCell(index+1);
9944 if (child)
9945 {
9946 // Find the position at the start of the child cell, since the table doesn't
9947 // have any caret position of its own.
8db2e3ef 9948 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
9949 }
9950 else
9951 return false;
9952}
9953
9954// Get the cell at the given character position (in the range of the table).
9955wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9956{
9957 int row = 0, col = 0;
9958 if (GetCellRowColumnPosition(pos, row, col))
9959 {
9960 return GetCell(row, col);
9961 }
9962 else
9963 return NULL;
9964}
9965
9966// Get the row/column for a given character position
9967bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9968{
9969 if (m_colCount == 0 || m_rowCount == 0)
9970 return false;
9971
9972 row = (int) (pos / m_colCount);
9973 col = pos - (row * m_colCount);
9974
9975 wxASSERT(row < m_rowCount && col < m_colCount);
9976
9977 if (row < m_rowCount && col < m_colCount)
9978 return true;
9979 else
9980 return false;
9981}
9982
9983// Calculate range, taking row/cell ordering into account instead of relying
9984// on list ordering.
9985void wxRichTextTable::CalculateRange(long start, long& end)
9986{
9987 long current = start;
9988 long lastEnd = current;
9989
9990 if (IsTopLevel())
9991 {
9992 current = 0;
9993 lastEnd = 0;
9994 }
9995
9996 int i, j;
9997 for (i = 0; i < m_rowCount; i++)
9998 {
9999 for (j = 0; j < m_colCount; j++)
10000 {
10001 wxRichTextCell* child = GetCell(i, j);
10002 if (child)
10003 {
10004 long childEnd = 0;
10005
10006 child->CalculateRange(current, childEnd);
10007
10008 lastEnd = childEnd;
10009 current = childEnd + 1;
10010 }
10011 }
10012 }
10013
10014 // A top-level object always has a range of size 1,
10015 // because its children don't count at this level.
10016 end = start;
10017 m_range.SetRange(start, start);
10018
10019 // An object with no children has zero length
10020 if (m_children.GetCount() == 0)
10021 lastEnd --;
10022 m_ownRange.SetRange(0, lastEnd);
10023}
10024
10025// Gets the range size.
914a4e23 10026bool 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 10027{
914a4e23 10028 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
603f702b
JS
10029}
10030
10031// Deletes content in the given range.
10032bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
10033{
10034 // TODO: implement deletion of cells
10035 return true;
10036}
10037
10038// Gets any text in this object for the given range.
10039wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10040{
10041 return wxRichTextBox::GetTextForRange(range);
10042}
10043
10044// Copies this object.
10045void wxRichTextTable::Copy(const wxRichTextTable& obj)
10046{
10047 wxRichTextBox::Copy(obj);
10048
10049 ClearTable();
10050
10051 m_rowCount = obj.m_rowCount;
10052 m_colCount = obj.m_colCount;
10053
10054 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10055
10056 int i, j;
10057 for (i = 0; i < m_rowCount; i++)
10058 {
10059 wxRichTextObjectPtrArray& colArray = m_cells[i];
10060 for (j = 0; j < m_colCount; j++)
10061 {
10062 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10063 AppendChild(cell);
10064
10065 colArray.Add(cell);
10066 }
10067 }
10068}
10069
10070void wxRichTextTable::ClearTable()
10071{
10072 m_cells.Clear();
10073 DeleteChildren();
10074}
10075
10076bool wxRichTextTable::CreateTable(int rows, int cols)
10077{
10078 ClearTable();
10079
10080 m_rowCount = rows;
10081 m_colCount = cols;
10082
10083 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10084
10085 int i, j;
10086 for (i = 0; i < rows; i++)
10087 {
10088 wxRichTextObjectPtrArray& colArray = m_cells[i];
10089 for (j = 0; j < cols; j++)
10090 {
10091 wxRichTextCell* cell = new wxRichTextCell;
10092 AppendChild(cell);
10093 cell->AddParagraph(wxEmptyString);
10094
10095 colArray.Add(cell);
10096 }
10097 }
10098
10099 return true;
10100}
10101
10102wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10103{
10104 wxASSERT(row < m_rowCount);
10105 wxASSERT(col < m_colCount);
10106
10107 if (row < m_rowCount && col < m_colCount)
10108 {
10109 wxRichTextObjectPtrArray& colArray = m_cells[row];
10110 wxRichTextObject* obj = colArray[col];
10111 return wxDynamicCast(obj, wxRichTextCell);
10112 }
10113 else
d67faa04 10114 return NULL;
603f702b
JS
10115}
10116
10117// Returns a selection object specifying the selections between start and end character positions.
10118// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10119wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10120{
10121 wxRichTextSelection selection;
10122 selection.SetContainer((wxRichTextTable*) this);
10123
10124 if (start > end)
10125 {
10126 long tmp = end;
10127 end = start;
10128 start = tmp;
10129 }
10130
10131 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10132
10133 if (end >= (m_colCount * m_rowCount))
10134 return selection;
10135
10136 // We need to find the rectangle of cells that is described by the rectangle
10137 // with start, end as the diagonal. Make sure we don't add cells that are
10138 // not currenty visible because they are overlapped by spanning cells.
10139/*
10140 --------------------------
10141 | 0 | 1 | 2 | 3 | 4 |
10142 --------------------------
10143 | 5 | 6 | 7 | 8 | 9 |
10144 --------------------------
10145 | 10 | 11 | 12 | 13 | 14 |
10146 --------------------------
10147 | 15 | 16 | 17 | 18 | 19 |
10148 --------------------------
10149
10150 Let's say we select 6 -> 18.
10151
10152 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10153 which is left and which is right.
10154
10155 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10156
10157 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10158 and (b) shown.
10159
10160
10161*/
10162
10163 int leftCol = start - m_colCount * int(start/m_colCount);
10164 int rightCol = end - m_colCount * int(end/m_colCount);
10165
10166 int topRow = int(start/m_colCount);
10167 int bottomRow = int(end/m_colCount);
10168
10169 if (leftCol > rightCol)
10170 {
10171 int tmp = rightCol;
10172 rightCol = leftCol;
10173 leftCol = tmp;
10174 }
10175
10176 if (topRow > bottomRow)
10177 {
10178 int tmp = bottomRow;
10179 bottomRow = topRow;
10180 topRow = tmp;
10181 }
10182
10183 int i, j;
10184 for (i = topRow; i <= bottomRow; i++)
10185 {
10186 for (j = leftCol; j <= rightCol; j++)
10187 {
10188 wxRichTextCell* cell = GetCell(i, j);
10189 if (cell && cell->IsShown())
10190 selection.Add(cell->GetRange());
10191 }
10192 }
10193
10194 return selection;
10195}
10196
10197// Sets the attributes for the cells specified by the selection.
10198bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10199{
10200 if (selection.GetContainer() != this)
10201 return false;
10202
10203 wxRichTextBuffer* buffer = GetBuffer();
10204 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10205 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10206
10207 if (withUndo)
10208 buffer->BeginBatchUndo(_("Set Cell Style"));
10209
10210 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10211 while (node)
10212 {
10213 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10214 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10215 SetStyle(cell, style, flags);
10216 node = node->GetNext();
10217 }
10218
10219 // Do action, or delay it until end of batch.
10220 if (withUndo)
10221 buffer->EndBatchUndo();
10222
10223 return true;
10224}
10225
10226bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10227{
10228 wxASSERT((startRow + noRows) < m_rowCount);
10229 if ((startRow + noRows) >= m_rowCount)
10230 return false;
10231
10232 int i, j;
10233 for (i = startRow; i < (startRow+noRows); i++)
10234 {
10235 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10236 for (j = 0; j < (int) colArray.GetCount(); j++)
10237 {
10238 wxRichTextObject* cell = colArray[j];
10239 RemoveChild(cell, true);
10240 }
10241
10242 // Keep deleting at the same position, since we move all
10243 // the others up
10244 m_cells.RemoveAt(startRow);
10245 }
10246
10247 m_rowCount = m_rowCount - noRows;
10248
10249 return true;
10250}
10251
10252bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10253{
10254 wxASSERT((startCol + noCols) < m_colCount);
10255 if ((startCol + noCols) >= m_colCount)
10256 return false;
10257
10258 bool deleteRows = (noCols == m_colCount);
10259
10260 int i, j;
10261 for (i = 0; i < m_rowCount; i++)
10262 {
10263 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
10264 for (j = startCol; j < (startCol+noCols); j++)
10265 {
10266 wxRichTextObject* cell = colArray[j];
10267 RemoveChild(cell, true);
10268 }
10269
10270 if (deleteRows)
10271 m_cells.RemoveAt(0);
10272 }
10273
10274 if (deleteRows)
10275 m_rowCount = 0;
10276 m_colCount = m_colCount - noCols;
10277
10278 return true;
10279}
10280
10281bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10282{
10283 wxASSERT(startRow <= m_rowCount);
10284 if (startRow > m_rowCount)
10285 return false;
10286
10287 int i, j;
10288 for (i = 0; i < noRows; i++)
10289 {
10290 int idx;
10291 if (startRow == m_rowCount)
10292 {
10293 m_cells.Add(wxRichTextObjectPtrArray());
10294 idx = m_cells.GetCount() - 1;
10295 }
10296 else
10297 {
10298 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10299 idx = startRow+i;
10300 }
10301
10302 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10303 for (j = 0; j < m_colCount; j++)
10304 {
10305 wxRichTextCell* cell = new wxRichTextCell;
10306 cell->GetAttributes() = attr;
10307
10308 AppendChild(cell);
10309 colArray.Add(cell);
10310 }
10311 }
10312
10313 m_rowCount = m_rowCount + noRows;
10314 return true;
10315}
10316
10317bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10318{
10319 wxASSERT(startCol <= m_colCount);
10320 if (startCol > m_colCount)
10321 return false;
10322
10323 int i, j;
10324 for (i = 0; i < m_rowCount; i++)
10325 {
10326 wxRichTextObjectPtrArray& colArray = m_cells[i];
10327 for (j = 0; j < noCols; j++)
10328 {
10329 wxRichTextCell* cell = new wxRichTextCell;
10330 cell->GetAttributes() = attr;
10331
10332 AppendChild(cell);
10333
10334 if (startCol == m_colCount)
10335 colArray.Add(cell);
10336 else
10337 colArray.Insert(cell, startCol+j);
10338 }
10339 }
10340
10341 m_colCount = m_colCount + noCols;
10342
10343 return true;
5ad9ae3a
JS
10344}
10345
603f702b
JS
10346// Edit properties via a GUI
10347bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 10348{
603f702b
JS
10349 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10350 boxDlg.SetAttributes(GetAttributes());
10351
10352 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 10353 {
603f702b
JS
10354 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10355 return true;
5ad9ae3a
JS
10356 }
10357 else
10358 return false;
bec80f4f
JS
10359}
10360
5d7836c4
JS
10361/*
10362 * Module to initialise and clean up handlers
10363 */
10364
10365class wxRichTextModule: public wxModule
10366{
10367DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10368public:
10369 wxRichTextModule() {}
cfa3b256
JS
10370 bool OnInit()
10371 {
d2d0adc7 10372 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
10373 wxRichTextBuffer::InitStandardHandlers();
10374 wxRichTextParagraph::InitDefaultTabs();
1aca9fcd
JS
10375
10376 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10377 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10378 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10379 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10380 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10381 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10382 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10385
cfa3b256 10386 return true;
47b378bd 10387 }
cfa3b256
JS
10388 void OnExit()
10389 {
10390 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 10391 wxRichTextBuffer::CleanUpDrawingHandlers();
7c9fdebe 10392 wxRichTextBuffer::CleanUpFieldTypes();
1aca9fcd 10393 wxRichTextXMLHandler::ClearNodeToClassMap();
cfa3b256
JS
10394 wxRichTextDecimalToRoman(-1);
10395 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 10396 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 10397 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 10398 }
5d7836c4
JS
10399};
10400
10401IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10402
10403
f1d6804f
RD
10404// If the richtext lib is dynamically loaded after the app has already started
10405// (such as from wxPython) then the built-in module system will not init this
10406// module. Provide this function to do it manually.
10407void wxRichTextModuleInit()
10408{
10409 wxModule* module = new wxRichTextModule;
10410 module->Init();
10411 wxModule::RegisterModule(module);
10412}
10413
10414
5d7836c4
JS
10415/*!
10416 * Commands for undo/redo
10417 *
10418 */
10419
10420wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 10421 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 10422{
603f702b 10423 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
10424}
10425
7fe8059f 10426wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
10427{
10428}
10429
10430wxRichTextCommand::~wxRichTextCommand()
10431{
10432 ClearActions();
10433}
10434
10435void wxRichTextCommand::AddAction(wxRichTextAction* action)
10436{
10437 if (!m_actions.Member(action))
10438 m_actions.Append(action);
10439}
10440
10441bool wxRichTextCommand::Do()
10442{
09f14108 10443 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
10444 {
10445 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10446 action->Do();
10447 }
10448
10449 return true;
10450}
10451
10452bool wxRichTextCommand::Undo()
10453{
09f14108 10454 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
10455 {
10456 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10457 action->Undo();
10458 }
10459
10460 return true;
10461}
10462
10463void wxRichTextCommand::ClearActions()
10464{
10465 WX_CLEAR_LIST(wxList, m_actions);
10466}
10467
10468/*!
10469 * Individual action
10470 *
10471 */
10472
603f702b
JS
10473wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10474 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10475 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
10476{
10477 m_buffer = buffer;
603f702b
JS
10478 m_object = NULL;
10479 m_containerAddress.Create(buffer, container);
5d7836c4
JS
10480 m_ignoreThis = ignoreFirstTime;
10481 m_cmdId = id;
10482 m_position = -1;
10483 m_ctrl = ctrl;
10484 m_name = name;
10485 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10486 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10487 if (cmd)
10488 cmd->AddAction(this);
10489}
10490
10491wxRichTextAction::~wxRichTextAction()
10492{
603f702b
JS
10493 if (m_object)
10494 delete m_object;
10495}
10496
10497// Returns the container that this action refers to, using the container address and top-level buffer.
10498wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10499{
10500 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10501 return container;
5d7836c4
JS
10502}
10503
603f702b 10504
7051fa41
JS
10505void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10506{
10507 // Store a list of line start character and y positions so we can figure out which area
10508 // we need to refresh
10509
10510#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
10511 wxRichTextParagraphLayoutBox* container = GetContainer();
10512 wxASSERT(container != NULL);
10513 if (!container)
10514 return;
10515
7051fa41
JS
10516 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10517 // If we had several actions, which only invalidate and leave layout until the
10518 // paint handler is called, then this might not be true. So we may need to switch
10519 // optimisation on only when we're simply adding text and not simultaneously
10520 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10521 // first, but of course this means we'll be doing it twice.
603f702b 10522 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41 10523 {
4ba36292
JS
10524 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10525 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
7051fa41
JS
10526 int lastY = firstVisiblePt.y + clientSize.y;
10527
603f702b
JS
10528 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10529 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
10530 while (node)
10531 {
10532 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10533 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10534 while (node2)
10535 {
10536 wxRichTextLine* line = node2->GetData();
10537 wxPoint pt = line->GetAbsolutePosition();
10538 wxRichTextRange range = line->GetAbsoluteRange();
10539
10540 if (pt.y > lastY)
10541 {
10542 node2 = wxRichTextLineList::compatibility_iterator();
10543 node = wxRichTextObjectList::compatibility_iterator();
10544 }
10545 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10546 {
10547 optimizationLineCharPositions.Add(range.GetStart());
10548 optimizationLineYPositions.Add(pt.y);
10549 }
10550
10551 if (node2)
10552 node2 = node2->GetNext();
10553 }
10554
10555 if (node)
10556 node = node->GetNext();
10557 }
10558 }
10559#endif
10560}
10561
5d7836c4
JS
10562bool wxRichTextAction::Do()
10563{
10564 m_buffer->Modify(true);
10565
603f702b
JS
10566 wxRichTextParagraphLayoutBox* container = GetContainer();
10567 wxASSERT(container != NULL);
10568 if (!container)
10569 return false;
10570
5d7836c4
JS
10571 switch (m_cmdId)
10572 {
10573 case wxRICHTEXT_INSERT:
10574 {
ea160b2e
JS
10575 // Store a list of line start character and y positions so we can figure out which area
10576 // we need to refresh
10577 wxArrayInt optimizationLineCharPositions;
10578 wxArrayInt optimizationLineYPositions;
10579
10580#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 10581 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
10582#endif
10583
603f702b
JS
10584 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10585 container->UpdateRanges();
10586
10587 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10588 // Layout() would stop prematurely at the top level.
10589 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 10590
603f702b 10591 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
10592
10593 // Character position to caret position
10594 newCaretPosition --;
10595
10596 // Don't take into account the last newline
5d7836c4
JS
10597 if (m_newParagraphs.GetPartialParagraph())
10598 newCaretPosition --;
46ee0e5b 10599 else
7c081bd2 10600 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
10601 {
10602 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10603 if (p->GetRange().GetLength() == 1)
10604 newCaretPosition --;
10605 }
5d7836c4 10606
603f702b 10607 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 10608
7051fa41 10609 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 10610
5912d19e
JS
10611 wxRichTextEvent cmdEvent(
10612 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10613 m_ctrl ? m_ctrl->GetId() : -1);
10614 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10615 cmdEvent.SetRange(GetRange());
10616 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10617 cmdEvent.SetContainer(container);
3e541562 10618
5912d19e 10619 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
10620
10621 break;
10622 }
10623 case wxRICHTEXT_DELETE:
10624 {
7051fa41
JS
10625 wxArrayInt optimizationLineCharPositions;
10626 wxArrayInt optimizationLineYPositions;
10627
10628#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10629 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10630#endif
10631
603f702b
JS
10632 container->DeleteRange(GetRange());
10633 container->UpdateRanges();
10634 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10635 // Layout() would stop prematurely at the top level.
10636 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 10637
6ccbca24 10638 long caretPos = GetRange().GetStart()-1;
603f702b 10639 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
10640 caretPos --;
10641
7051fa41 10642 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 10643
5912d19e
JS
10644 wxRichTextEvent cmdEvent(
10645 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10646 m_ctrl ? m_ctrl->GetId() : -1);
10647 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10648 cmdEvent.SetRange(GetRange());
10649 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10650 cmdEvent.SetContainer(container);
3e541562 10651
5912d19e
JS
10652 m_buffer->SendEvent(cmdEvent);
10653
5d7836c4
JS
10654 break;
10655 }
10656 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10657 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10658 {
10659 ApplyParagraphs(GetNewParagraphs());
603f702b 10660
c4168888 10661 // Invalidate the whole buffer if there were floating objects
e12b91a3 10662 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10663 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10664 else
10665 {
10666 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10667 // Layout() would stop prematurely at the top level.
10668 container->InvalidateHierarchy(GetRange());
10669 }
603f702b
JS
10670
10671 UpdateAppearance(GetPosition());
10672
10673 wxRichTextEvent cmdEvent(
590a0f8b 10674 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
10675 m_ctrl ? m_ctrl->GetId() : -1);
10676 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10677 cmdEvent.SetRange(GetRange());
10678 cmdEvent.SetPosition(GetRange().GetStart());
10679 cmdEvent.SetContainer(container);
10680
10681 m_buffer->SendEvent(cmdEvent);
10682
10683 break;
10684 }
10685 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10686 {
10687 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10688 if (obj)
10689 {
10690 wxRichTextAttr oldAttr = obj->GetAttributes();
10691 obj->GetAttributes() = m_attributes;
10692 m_attributes = oldAttr;
10693 }
10694
10695 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10696 // Layout() would stop prematurely at the top level.
c4168888 10697 // Invalidate the whole buffer if there were floating objects
e12b91a3 10698 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10699 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10700 else
10701 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10702
10703 UpdateAppearance(GetPosition());
10704
5912d19e
JS
10705 wxRichTextEvent cmdEvent(
10706 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10707 m_ctrl ? m_ctrl->GetId() : -1);
10708 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10709 cmdEvent.SetRange(GetRange());
10710 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10711 cmdEvent.SetContainer(container);
3e541562 10712
5912d19e
JS
10713 m_buffer->SendEvent(cmdEvent);
10714
603f702b
JS
10715 break;
10716 }
10717 case wxRICHTEXT_CHANGE_OBJECT:
10718 {
10719 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10720 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10721 if (obj && m_object)
10722 {
10723 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10724 if (node)
10725 {
10726 wxRichTextObject* obj = node->GetData();
10727 node->SetData(m_object);
10728 m_object = obj;
10729 }
10730 }
10731
10732 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10733 // Layout() would stop prematurely at the top level.
c4168888 10734 // Invalidate the whole buffer if there were floating objects
e12b91a3 10735 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10736 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10737 else
10738 container->InvalidateHierarchy(GetRange());
603f702b
JS
10739
10740 UpdateAppearance(GetPosition());
10741
10742 // TODO: send new kind of modification event
10743
5d7836c4
JS
10744 break;
10745 }
10746 default:
10747 break;
10748 }
10749
10750 return true;
10751}
10752
10753bool wxRichTextAction::Undo()
10754{
10755 m_buffer->Modify(true);
10756
603f702b
JS
10757 wxRichTextParagraphLayoutBox* container = GetContainer();
10758 wxASSERT(container != NULL);
10759 if (!container)
10760 return false;
10761
5d7836c4
JS
10762 switch (m_cmdId)
10763 {
10764 case wxRICHTEXT_INSERT:
10765 {
7051fa41
JS
10766 wxArrayInt optimizationLineCharPositions;
10767 wxArrayInt optimizationLineYPositions;
10768
10769#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10770 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10771#endif
10772
603f702b
JS
10773 container->DeleteRange(GetRange());
10774 container->UpdateRanges();
7c9fdebe 10775
603f702b
JS
10776 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10777 // Layout() would stop prematurely at the top level.
10778 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
10779
10780 long newCaretPosition = GetPosition() - 1;
3e541562 10781
7051fa41 10782 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10783
5912d19e
JS
10784 wxRichTextEvent cmdEvent(
10785 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10786 m_ctrl ? m_ctrl->GetId() : -1);
10787 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10788 cmdEvent.SetRange(GetRange());
10789 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10790 cmdEvent.SetContainer(container);
3e541562 10791
5912d19e
JS
10792 m_buffer->SendEvent(cmdEvent);
10793
5d7836c4
JS
10794 break;
10795 }
10796 case wxRICHTEXT_DELETE:
10797 {
7051fa41
JS
10798 wxArrayInt optimizationLineCharPositions;
10799 wxArrayInt optimizationLineYPositions;
10800
10801#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10802 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10803#endif
10804
603f702b
JS
10805 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10806 container->UpdateRanges();
7c9fdebe 10807
603f702b
JS
10808 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10809 // Layout() would stop prematurely at the top level.
10810 container->InvalidateHierarchy(GetRange());
5d7836c4 10811
7051fa41 10812 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10813
5912d19e
JS
10814 wxRichTextEvent cmdEvent(
10815 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10816 m_ctrl ? m_ctrl->GetId() : -1);
10817 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10818 cmdEvent.SetRange(GetRange());
10819 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10820 cmdEvent.SetContainer(container);
3e541562 10821
5912d19e
JS
10822 m_buffer->SendEvent(cmdEvent);
10823
5d7836c4
JS
10824 break;
10825 }
10826 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10827 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10828 {
10829 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
10830 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10831 // Layout() would stop prematurely at the top level.
10832 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10833
10834 UpdateAppearance(GetPosition());
10835
5912d19e 10836 wxRichTextEvent cmdEvent(
590a0f8b 10837 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
10838 m_ctrl ? m_ctrl->GetId() : -1);
10839 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10840 cmdEvent.SetRange(GetRange());
10841 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10842 cmdEvent.SetContainer(container);
3e541562 10843
5912d19e
JS
10844 m_buffer->SendEvent(cmdEvent);
10845
5d7836c4
JS
10846 break;
10847 }
603f702b
JS
10848 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10849 case wxRICHTEXT_CHANGE_OBJECT:
10850 {
10851 return Do();
10852 }
5d7836c4
JS
10853 default:
10854 break;
10855 }
10856
10857 return true;
10858}
10859
10860/// Update the control appearance
603f702b 10861void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 10862{
603f702b
JS
10863 wxRichTextParagraphLayoutBox* container = GetContainer();
10864 wxASSERT(container != NULL);
10865 if (!container)
10866 return;
10867
5d7836c4
JS
10868 if (m_ctrl)
10869 {
603f702b 10870 m_ctrl->SetFocusObject(container);
5d7836c4 10871 m_ctrl->SetCaretPosition(caretPosition);
603f702b 10872
5d7836c4
JS
10873 if (!m_ctrl->IsFrozen())
10874 {
603f702b
JS
10875 wxRect containerRect = container->GetRect();
10876
2f36e8dc 10877 m_ctrl->LayoutContent();
5d7836c4 10878
603f702b
JS
10879 // Refresh everything if there were floating objects or the container changed size
10880 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
e12b91a3 10881 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
603f702b
JS
10882 {
10883 m_ctrl->Refresh(false);
10884 }
10885 else
10886
10887#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10888 // Find refresh rectangle if we are in a position to optimise refresh
10889 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10890 {
10891 size_t i;
10892
4ba36292
JS
10893 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10894 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
603f702b
JS
10895
10896 // Start/end positions
10897 int firstY = 0;
10898 int lastY = firstVisiblePt.y + clientSize.y;
10899
10900 bool foundEnd = false;
10901
10902 // position offset - how many characters were inserted
10903 int positionOffset = GetRange().GetLength();
10904
10905 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10906 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10907 positionOffset = - positionOffset;
10908
10909 // find the first line which is being drawn at the same position as it was
10910 // before. Since we're talking about a simple insertion, we can assume
10911 // that the rest of the window does not need to be redrawn.
10912
10913 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10914 // Since we support floating layout, we should redraw the whole para instead of just
10915 // the first line touching the invalid range.
10916 if (para)
10917 {
10918 firstY = para->GetPosition().y;
10919 }
10920
10921 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10922 while (node)
10923 {
10924 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10925 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10926 while (node2)
10927 {
10928 wxRichTextLine* line = node2->GetData();
10929 wxPoint pt = line->GetAbsolutePosition();
10930 wxRichTextRange range = line->GetAbsoluteRange();
10931
10932 // we want to find the first line that is in the same position
10933 // as before. This will mean we're at the end of the changed text.
10934
10935 if (pt.y > lastY) // going past the end of the window, no more info
10936 {
10937 node2 = wxRichTextLineList::compatibility_iterator();
10938 node = wxRichTextObjectList::compatibility_iterator();
10939 }
10940 // Detect last line in the buffer
10941 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10942 {
10943 // If deleting text, make sure we refresh below as well as above
10944 if (positionOffset >= 0)
10945 {
10946 foundEnd = true;
10947 lastY = pt.y + line->GetSize().y;
10948 }
10949
10950 node2 = wxRichTextLineList::compatibility_iterator();
10951 node = wxRichTextObjectList::compatibility_iterator();
10952
10953 break;
10954 }
10955 else
10956 {
10957 // search for this line being at the same position as before
10958 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10959 {
10960 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10961 ((*optimizationLineYPositions)[i] == pt.y))
10962 {
10963 // Stop, we're now the same as we were
10964 foundEnd = true;
10965
10966 lastY = pt.y;
10967
10968 node2 = wxRichTextLineList::compatibility_iterator();
10969 node = wxRichTextObjectList::compatibility_iterator();
10970
10971 break;
10972 }
10973 }
10974 }
10975
10976 if (node2)
10977 node2 = node2->GetNext();
10978 }
10979
10980 if (node)
10981 node = node->GetNext();
10982 }
10983
10984 firstY = wxMax(firstVisiblePt.y, firstY);
10985 if (!foundEnd)
10986 lastY = firstVisiblePt.y + clientSize.y;
10987
10988 // Convert to device coordinates
4ba36292 10989 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
603f702b
JS
10990 m_ctrl->RefreshRect(rect);
10991 }
10992 else
1c13f06e 10993#endif
603f702b
JS
10994 m_ctrl->Refresh(false);
10995
10996 m_ctrl->PositionCaret();
4fe83b93
JS
10997
10998 // This causes styles to persist when doing programmatic
10999 // content creation except when Freeze/Thaw is used, so
11000 // disable this and check for the consequences.
11001 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 11002
5d7836c4 11003 if (sendUpdateEvent)
0ec1179b 11004 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 11005 }
7fe8059f 11006 }
5d7836c4
JS
11007}
11008
11009/// Replace the buffer paragraphs with the new ones.
0ca07313 11010void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 11011{
603f702b
JS
11012 wxRichTextParagraphLayoutBox* container = GetContainer();
11013 wxASSERT(container != NULL);
11014 if (!container)
11015 return;
11016
5d7836c4
JS
11017 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
11018 while (node)
11019 {
11020 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
11021 wxASSERT (para != NULL);
11022
11023 // We'll replace the existing paragraph by finding the paragraph at this position,
11024 // delete its node data, and setting a copy as the new node data.
11025 // TODO: make more efficient by simply swapping old and new paragraph objects.
11026
603f702b 11027 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
11028 if (existingPara)
11029 {
603f702b 11030 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
11031 if (bufferParaNode)
11032 {
11033 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 11034 newPara->SetParent(container);
5d7836c4
JS
11035
11036 bufferParaNode->SetData(newPara);
11037
11038 delete existingPara;
11039 }
11040 }
11041
11042 node = node->GetNext();
11043 }
11044}
11045
11046
11047/*!
11048 * wxRichTextRange
11049 * This stores beginning and end positions for a range of data.
11050 */
11051
603f702b
JS
11052WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11053
5d7836c4
JS
11054/// Limit this range to be within 'range'
11055bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11056{
11057 if (m_start < range.m_start)
11058 m_start = range.m_start;
11059
11060 if (m_end > range.m_end)
11061 m_end = range.m_end;
11062
11063 return true;
11064}
11065
11066/*!
11067 * wxRichTextImage implementation
11068 * This object represents an image.
11069 */
11070
bec80f4f 11071IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 11072
24777478 11073wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11074 wxRichTextObject(parent)
5d7836c4 11075{
23698b12 11076 Init();
cdaed652 11077 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
11078 if (charStyle)
11079 SetAttributes(*charStyle);
5d7836c4
JS
11080}
11081
24777478 11082wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11083 wxRichTextObject(parent)
5d7836c4 11084{
23698b12 11085 Init();
5d7836c4 11086 m_imageBlock = imageBlock;
4f32b3cf
JS
11087 if (charStyle)
11088 SetAttributes(*charStyle);
5d7836c4
JS
11089}
11090
914a4e23
JS
11091wxRichTextImage::~wxRichTextImage()
11092{
11093}
11094
23698b12
JS
11095void wxRichTextImage::Init()
11096{
11097 m_originalImageSize = wxSize(-1, -1);
11098}
11099
cdaed652 11100/// Create a cached image at the required size
914a4e23 11101bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
5d7836c4 11102{
23698b12
JS
11103 if (!m_imageBlock.IsOk())
11104 return false;
11105
11106 // If we have an original image size, use that to compute the cached bitmap size
11107 // instead of loading the image each time. This way we can avoid loading
11108 // the image so long as the new cached bitmap size hasn't changed.
11109
11110 wxImage image;
2798df59 11111 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
cdaed652 11112 {
23698b12 11113 m_imageCache = wxNullBitmap;
ce00f59b 11114
cdaed652
VZ
11115 m_imageBlock.Load(image);
11116 if (!image.IsOk())
11117 return false;
ce00f59b 11118
23698b12
JS
11119 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11120 }
11121
11122 int width = m_originalImageSize.GetWidth();
11123 int height = m_originalImageSize.GetHeight();
11124
11125 int parentWidth = 0;
11126 int parentHeight = 0;
bec80f4f 11127
23698b12
JS
11128 int maxWidth = -1;
11129 int maxHeight = -1;
11130
914a4e23
JS
11131 wxSize sz = parentSize;
11132 if (sz == wxDefaultSize)
23698b12 11133 {
914a4e23
JS
11134 if (GetParent() && GetParent()->GetParent())
11135 sz = GetParent()->GetParent()->GetCachedSize();
11136 }
ab6b1860 11137
914a4e23
JS
11138 if (sz != wxDefaultSize)
11139 {
11140 wxRichTextBuffer* buffer = GetBuffer();
11141 if (buffer)
11142 {
11143 // Find the actual space available when margin is taken into account
23698b12
JS
11144 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11145 marginRect = wxRect(0, 0, sz.x, sz.y);
914a4e23
JS
11146 if (GetParent() && GetParent()->GetParent())
11147 {
11148 buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11149 sz = contentRect.GetSize();
11150 }
23698b12 11151
914a4e23
JS
11152 // Use a minimum size to stop images becoming very small
11153 parentWidth = wxMax(100, sz.GetWidth());
11154 parentHeight = wxMax(100, sz.GetHeight());
23698b12 11155
914a4e23
JS
11156 if (buffer->GetRichTextCtrl())
11157 // Start with a maximum width of the control size, even if not specified by the content,
11158 // to minimize the amount of picture overlapping the right-hand side
11159 maxWidth = parentWidth;
cdaed652 11160 }
23698b12
JS
11161 }
11162
11163 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11164 {
11165 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11166 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11167 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11168 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11169 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11170 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11171 }
11172
11173 // Limit to max width
11174
11175 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11176 {
11177 int mw = -1;
11178
11179 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11180 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11181 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11182 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11183 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11184 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11185
11186 // If we already have a smaller max width due to the constraints of the control size,
11187 // don't use the larger max width.
11188 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11189 maxWidth = mw;
11190 }
11191
11192 if (maxWidth > 0 && width > maxWidth)
11193 width = maxWidth;
11194
11195 // Preserve the aspect ratio
11196 if (width != m_originalImageSize.GetWidth())
11197 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11198
11199 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11200 {
11201 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11202 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11203 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11204 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11205 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11206 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11207
11208 // Preserve the aspect ratio
11209 if (height != m_originalImageSize.GetHeight())
11210 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11211 }
11212
11213 // Limit to max height
11214
11215 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11216 {
11217 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11218 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11219 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11220 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11221 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11222 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11223 }
11224
11225 if (maxHeight > 0 && height > maxHeight)
11226 {
11227 height = maxHeight;
11228
11229 // Preserve the aspect ratio
11230 if (height != m_originalImageSize.GetHeight())
11231 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11232 }
11233
ab6b1860
JS
11234 // Prevent the use of zero size
11235 width = wxMax(1, width);
11236 height = wxMax(1, height);
11237
23698b12
JS
11238 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11239 {
11240 // Do nothing, we didn't need to change the image cache
11241 }
11242 else
11243 {
11244 if (!image.IsOk())
cdaed652 11245 {
23698b12
JS
11246 m_imageBlock.Load(image);
11247 if (!image.IsOk())
11248 return false;
cdaed652 11249 }
5d7836c4 11250
cdaed652
VZ
11251 if (image.GetWidth() == width && image.GetHeight() == height)
11252 m_imageCache = wxBitmap(image);
11253 else
11254 {
11255 // If the original width and height is small, e.g. 400 or below,
11256 // scale up and then down to improve image quality. This can make
11257 // a big difference, with not much performance hit.
11258 int upscaleThreshold = 400;
11259 wxImage img;
11260 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11261 {
11262 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11263 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11264 }
11265 else
11266 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11267 m_imageCache = wxBitmap(img);
11268 }
11269 }
ce00f59b 11270
cdaed652 11271 return m_imageCache.IsOk();
5d7836c4
JS
11272}
11273
5d7836c4 11274/// Draw the item
20d09da5 11275bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 11276{
603f702b
JS
11277 if (!IsShown())
11278 return true;
11279
cdaed652
VZ
11280 // Don't need cached size AFAIK
11281 // wxSize size = GetCachedSize();
11282 if (!LoadImageCache(dc))
5d7836c4 11283 return false;
ce00f59b 11284
8db2e3ef
JS
11285 wxRichTextAttr attr(GetAttributes());
11286 context.ApplyVirtualAttributes(attr, this);
11287
11288 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b 11289
603f702b
JS
11290 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11291 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11292 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 11293 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11294
11295 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 11296
a70eb13e 11297 if (selection.WithinSelection(GetRange().GetStart(), this))
5d7836c4 11298 {
ecb5fbf1
JS
11299 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11300 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 11301 dc.SetLogicalFunction(wxINVERT);
603f702b 11302 dc.DrawRectangle(contentRect);
5d7836c4
JS
11303 dc.SetLogicalFunction(wxCOPY);
11304 }
11305
11306 return true;
11307}
11308
11309/// Lay the item out
8db2e3ef 11310bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 11311{
cdaed652
VZ
11312 if (!LoadImageCache(dc))
11313 return false;
5d7836c4 11314
603f702b
JS
11315 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11316 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11317 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
11318
11319 wxRichTextAttr attr(GetAttributes());
11320 context.ApplyVirtualAttributes(attr, this);
11321
11322 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11323
11324 wxSize overallSize = marginRect.GetSize();
11325
11326 SetCachedSize(overallSize);
11327 SetMaxSize(overallSize);
11328 SetMinSize(overallSize);
cdaed652 11329 SetPosition(rect.GetPosition());
5d7836c4
JS
11330
11331 return true;
11332}
11333
11334/// Get/set the object size for the given range. Returns false if the range
11335/// is invalid for this object.
914a4e23 11336bool 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
11337{
11338 if (!range.IsWithin(GetRange()))
11339 return false;
11340
914a4e23 11341 if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
31778480 11342 {
cdaed652
VZ
11343 size.x = 0; size.y = 0;
11344 if (partialExtents)
31778480 11345 partialExtents->Add(0);
cdaed652 11346 return false;
31778480 11347 }
ce00f59b 11348
8db2e3ef
JS
11349 wxRichTextAttr attr(GetAttributes());
11350 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11351
603f702b
JS
11352 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11353 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11354 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef 11355 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11356
11357 wxSize overallSize = marginRect.GetSize();
31778480 11358
cdaed652 11359 if (partialExtents)
603f702b 11360 partialExtents->Add(overallSize.x);
5d7836c4 11361
603f702b 11362 size = overallSize;
5d7836c4
JS
11363
11364 return true;
11365}
11366
603f702b
JS
11367// Get the 'natural' size for an object. For an image, it would be the
11368// image size.
11369wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11370{
11371 wxTextAttrSize size;
11372 if (GetImageCache().IsOk())
11373 {
11374 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11375 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11376 }
11377 return size;
11378}
11379
11380
5d7836c4
JS
11381/// Copy
11382void wxRichTextImage::Copy(const wxRichTextImage& obj)
11383{
bec80f4f 11384 wxRichTextObject::Copy(obj);
59509217 11385
5d7836c4 11386 m_imageBlock = obj.m_imageBlock;
23698b12 11387 m_originalImageSize = obj.m_originalImageSize;
5d7836c4
JS
11388}
11389
cdaed652
VZ
11390/// Edit properties via a GUI
11391bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11392{
603f702b
JS
11393 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11394 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
11395
11396 if (imageDlg.ShowModal() == wxID_OK)
11397 {
603f702b
JS
11398 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11399 // indeterminate in the object.
11400 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
11401 return true;
11402 }
11403 else
11404 return false;
11405}
11406
5d7836c4
JS
11407/*!
11408 * Utilities
11409 *
11410 */
11411
11412/// Compare two attribute objects
24777478 11413bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 11414{
38f833b1 11415 return (attr1 == attr2);
5d7836c4
JS
11416}
11417
44cc96a8
JS
11418/// Compare tabs
11419bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11420{
11421 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
11422 return false;
11423
44cc96a8
JS
11424 size_t i;
11425 for (i = 0; i < tabs1.GetCount(); i++)
11426 {
11427 if (tabs1[i] != tabs2[i])
11428 return false;
11429 }
11430 return true;
11431}
5d7836c4 11432
24777478 11433bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
11434{
11435 return destStyle.Apply(style, compareWith);
11436}
5d7836c4 11437
44cc96a8 11438// Remove attributes
24777478 11439bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 11440{
24777478 11441 return destStyle.RemoveStyle(style);
44cc96a8 11442}
5d7836c4 11443
44cc96a8
JS
11444/// Combine two bitlists, specifying the bits of interest with separate flags.
11445bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11446{
24777478 11447 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 11448}
5d7836c4 11449
44cc96a8
JS
11450/// Compare two bitlists
11451bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11452{
24777478 11453 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 11454}
38f833b1 11455
44cc96a8 11456/// Split into paragraph and character styles
24777478 11457bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 11458{
24777478 11459 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 11460}
5d7836c4 11461
44cc96a8
JS
11462/// Convert a decimal to Roman numerals
11463wxString wxRichTextDecimalToRoman(long n)
11464{
11465 static wxArrayInt decimalNumbers;
11466 static wxArrayString romanNumbers;
5d7836c4 11467
44cc96a8
JS
11468 // Clean up arrays
11469 if (n == -1)
11470 {
11471 decimalNumbers.Clear();
11472 romanNumbers.Clear();
11473 return wxEmptyString;
11474 }
5d7836c4 11475
44cc96a8
JS
11476 if (decimalNumbers.GetCount() == 0)
11477 {
11478 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 11479
44cc96a8
JS
11480 wxRichTextAddDecRom(1000, wxT("M"));
11481 wxRichTextAddDecRom(900, wxT("CM"));
11482 wxRichTextAddDecRom(500, wxT("D"));
11483 wxRichTextAddDecRom(400, wxT("CD"));
11484 wxRichTextAddDecRom(100, wxT("C"));
11485 wxRichTextAddDecRom(90, wxT("XC"));
11486 wxRichTextAddDecRom(50, wxT("L"));
11487 wxRichTextAddDecRom(40, wxT("XL"));
11488 wxRichTextAddDecRom(10, wxT("X"));
11489 wxRichTextAddDecRom(9, wxT("IX"));
11490 wxRichTextAddDecRom(5, wxT("V"));
11491 wxRichTextAddDecRom(4, wxT("IV"));
11492 wxRichTextAddDecRom(1, wxT("I"));
11493 }
5d7836c4 11494
44cc96a8
JS
11495 int i = 0;
11496 wxString roman;
ea160b2e 11497
44cc96a8 11498 while (n > 0 && i < 13)
42688aea 11499 {
44cc96a8
JS
11500 if (n >= decimalNumbers[i])
11501 {
11502 n -= decimalNumbers[i];
11503 roman += romanNumbers[i];
11504 }
11505 else
11506 {
11507 i ++;
11508 }
42688aea 11509 }
44cc96a8
JS
11510 if (roman.IsEmpty())
11511 roman = wxT("0");
11512 return roman;
11513}
42688aea 11514
44cc96a8
JS
11515/*!
11516 * wxRichTextFileHandler
11517 * Base class for file handlers
11518 */
4d6d8bf4 11519
44cc96a8 11520IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 11521
44cc96a8
JS
11522#if wxUSE_FFILE && wxUSE_STREAMS
11523bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 11524{
44cc96a8 11525 wxFFileInputStream stream(filename);
a1b806b9 11526 if (stream.IsOk())
44cc96a8 11527 return LoadFile(buffer, stream);
5d7836c4 11528
44cc96a8
JS
11529 return false;
11530}
5d7836c4 11531
44cc96a8
JS
11532bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11533{
11534 wxFFileOutputStream stream(filename);
a1b806b9 11535 if (stream.IsOk())
44cc96a8 11536 return SaveFile(buffer, stream);
5d7836c4 11537
44cc96a8
JS
11538 return false;
11539}
11540#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 11541
44cc96a8
JS
11542/// Can we handle this filename (if using files)? By default, checks the extension.
11543bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11544{
11545 wxString path, file, ext;
a51e601e 11546 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 11547
44cc96a8
JS
11548 return (ext.Lower() == GetExtension());
11549}
5d7836c4 11550
44cc96a8
JS
11551/*!
11552 * wxRichTextTextHandler
11553 * Plain text handler
11554 */
5d7836c4 11555
44cc96a8 11556IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 11557
44cc96a8
JS
11558#if wxUSE_STREAMS
11559bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11560{
11561 if (!stream.IsOk())
797e38dd
JS
11562 return false;
11563
44cc96a8
JS
11564 wxString str;
11565 int lastCh = 0;
5d7836c4 11566
44cc96a8
JS
11567 while (!stream.Eof())
11568 {
11569 int ch = stream.GetC();
5d7836c4 11570
44cc96a8
JS
11571 if (!stream.Eof())
11572 {
11573 if (ch == 10 && lastCh != 13)
11574 str += wxT('\n');
5d7836c4 11575
44cc96a8
JS
11576 if (ch > 0 && ch != 10)
11577 str += wxChar(ch);
5d7836c4 11578
44cc96a8
JS
11579 lastCh = ch;
11580 }
11581 }
5d7836c4 11582
44cc96a8
JS
11583 buffer->ResetAndClearCommands();
11584 buffer->Clear();
11585 buffer->AddParagraphs(str);
11586 buffer->UpdateRanges();
5d7836c4 11587
44cc96a8
JS
11588 return true;
11589}
5d7836c4 11590
44cc96a8
JS
11591bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11592{
11593 if (!stream.IsOk())
5d7836c4
JS
11594 return false;
11595
44cc96a8 11596 wxString text = buffer->GetText();
38f833b1 11597
44cc96a8
JS
11598 wxString newLine = wxRichTextLineBreakChar;
11599 text.Replace(newLine, wxT("\n"));
5d7836c4 11600
44cc96a8 11601 wxCharBuffer buf = text.ToAscii();
5d7836c4 11602
44cc96a8
JS
11603 stream.Write((const char*) buf, text.length());
11604 return true;
11605}
11606#endif // wxUSE_STREAMS
5d7836c4 11607
44cc96a8
JS
11608/*
11609 * Stores information about an image, in binary in-memory form
11610 */
59509217 11611
44cc96a8
JS
11612wxRichTextImageBlock::wxRichTextImageBlock()
11613{
11614 Init();
11615}
5d7836c4 11616
44cc96a8
JS
11617wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11618{
11619 Init();
11620 Copy(block);
11621}
ea160b2e 11622
44cc96a8
JS
11623wxRichTextImageBlock::~wxRichTextImageBlock()
11624{
5276b0a5 11625 wxDELETEA(m_data);
5d7836c4
JS
11626}
11627
44cc96a8 11628void wxRichTextImageBlock::Init()
5d7836c4
JS
11629{
11630 m_data = NULL;
11631 m_dataSize = 0;
d75a69e8 11632 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11633}
11634
11635void wxRichTextImageBlock::Clear()
11636{
5276b0a5 11637 wxDELETEA(m_data);
5d7836c4 11638 m_dataSize = 0;
d75a69e8 11639 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11640}
11641
11642
11643// Load the original image into a memory block.
11644// If the image is not a JPEG, we must convert it into a JPEG
11645// to conserve space.
11646// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11647// load the image a 2nd time.
11648
d75a69e8
FM
11649bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11650 wxImage& image, bool convertToJPEG)
5d7836c4
JS
11651{
11652 m_imageType = imageType;
11653
11654 wxString filenameToRead(filename);
7fe8059f 11655 bool removeFile = false;
5d7836c4 11656
62891c87 11657 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11658 return false; // Could not determine image type
5d7836c4
JS
11659
11660 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11661 {
a51e601e
FM
11662 wxString tempFile =
11663 wxFileName::CreateTempFileName(_("image"));
5d7836c4 11664
a51e601e 11665 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11666
11667 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11668 filenameToRead = tempFile;
7fe8059f 11669 removeFile = true;
5d7836c4
JS
11670
11671 m_imageType = wxBITMAP_TYPE_JPEG;
11672 }
11673 wxFile file;
11674 if (!file.Open(filenameToRead))
7fe8059f 11675 return false;
5d7836c4
JS
11676
11677 m_dataSize = (size_t) file.Length();
11678 file.Close();
11679
11680 if (m_data)
11681 delete[] m_data;
11682 m_data = ReadBlock(filenameToRead, m_dataSize);
11683
11684 if (removeFile)
11685 wxRemoveFile(filenameToRead);
11686
11687 return (m_data != NULL);
11688}
11689
11690// Make an image block from the wxImage in the given
11691// format.
d75a69e8 11692bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 11693{
5d7836c4
JS
11694 image.SetOption(wxT("quality"), quality);
11695
62891c87 11696 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11697 return false; // Could not determine image type
5d7836c4 11698
cdaed652
VZ
11699 return DoMakeImageBlock(image, imageType);
11700}
11701
11702// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11703bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11704{
11705 if (imageType == wxBITMAP_TYPE_INVALID)
11706 return false; // Could not determine image type
ce00f59b 11707
cdaed652
VZ
11708 return DoMakeImageBlock(image, imageType);
11709}
7fe8059f 11710
cdaed652
VZ
11711// Makes the image block
11712bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11713{
11714 wxMemoryOutputStream memStream;
11715 if (!image.SaveFile(memStream, imageType))
5d7836c4 11716 {
7fe8059f 11717 return false;
5d7836c4 11718 }
ce00f59b 11719
cdaed652
VZ
11720 unsigned char* block = new unsigned char[memStream.GetSize()];
11721 if (!block)
377c1ba4 11722 return false;
ce00f59b 11723
5d7836c4
JS
11724 if (m_data)
11725 delete[] m_data;
cdaed652 11726 m_data = block;
ce00f59b
VZ
11727
11728 m_imageType = imageType;
cdaed652 11729 m_dataSize = memStream.GetSize();
5d7836c4 11730
cdaed652 11731 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
11732
11733 return (m_data != NULL);
11734}
11735
5d7836c4
JS
11736// Write to a file
11737bool wxRichTextImageBlock::Write(const wxString& filename)
11738{
11739 return WriteBlock(filename, m_data, m_dataSize);
11740}
11741
11742void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11743{
11744 m_imageType = block.m_imageType;
5276b0a5 11745 wxDELETEA(m_data);
5d7836c4
JS
11746 m_dataSize = block.m_dataSize;
11747 if (m_dataSize == 0)
11748 return;
11749
11750 m_data = new unsigned char[m_dataSize];
11751 unsigned int i;
11752 for (i = 0; i < m_dataSize; i++)
11753 m_data[i] = block.m_data[i];
11754}
11755
11756//// Operators
11757void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11758{
11759 Copy(block);
11760}
11761
11762// Load a wxImage from the block
11763bool wxRichTextImageBlock::Load(wxImage& image)
11764{
11765 if (!m_data)
7fe8059f 11766 return false;
5d7836c4
JS
11767
11768 // Read in the image.
0ca07313 11769#if wxUSE_STREAMS
5d7836c4
JS
11770 wxMemoryInputStream mstream(m_data, m_dataSize);
11771 bool success = image.LoadFile(mstream, GetImageType());
11772#else
a51e601e
FM
11773 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11774 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11775
11776 if (!WriteBlock(tempFile, m_data, m_dataSize))
11777 {
7fe8059f 11778 return false;
5d7836c4
JS
11779 }
11780 success = image.LoadFile(tempFile, GetImageType());
11781 wxRemoveFile(tempFile);
11782#endif
11783
11784 return success;
11785}
11786
11787// Write data in hex to a stream
11788bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11789{
4dc7ae1a
JS
11790 if (m_dataSize == 0)
11791 return true;
11792
11793 int bufSize = 100000;
a3c12576
JS
11794 if (int(2*m_dataSize) < bufSize)
11795 bufSize = 2*m_dataSize;
4dc7ae1a 11796 char* buf = new char[bufSize+1];
351c0647
JS
11797
11798 int left = m_dataSize;
11799 int n, i, j;
11800 j = 0;
11801 while (left > 0)
5d7836c4 11802 {
351c0647
JS
11803 if (left*2 > bufSize)
11804 {
11805 n = bufSize; left -= (bufSize/2);
11806 }
11807 else
11808 {
11809 n = left*2; left = 0;
11810 }
7fe8059f 11811
351c0647
JS
11812 char* b = buf;
11813 for (i = 0; i < (n/2); i++)
11814 {
f728025e 11815 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
11816 b += 2; j ++;
11817 }
5d7836c4 11818
351c0647
JS
11819 buf[n] = 0;
11820 stream.Write((const char*) buf, n);
11821 }
4dc7ae1a 11822 delete[] buf;
5d7836c4
JS
11823 return true;
11824}
11825
11826// Read data in hex from a stream
d75a69e8 11827bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
11828{
11829 int dataSize = length/2;
11830
11831 if (m_data)
11832 delete[] m_data;
11833
046fce47
FM
11834 // create a null terminated temporary string:
11835 char str[3];
11836 str[2] = '\0';
11837
5d7836c4
JS
11838 m_data = new unsigned char[dataSize];
11839 int i;
11840 for (i = 0; i < dataSize; i ++)
11841 {
c9f78968
VS
11842 str[0] = (char)stream.GetC();
11843 str[1] = (char)stream.GetC();
5d7836c4 11844
a9465653 11845 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
11846 }
11847
11848 m_dataSize = dataSize;
11849 m_imageType = imageType;
11850
11851 return true;
11852}
11853
5d7836c4
JS
11854// Allocate and read from stream as a block of memory
11855unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11856{
11857 unsigned char* block = new unsigned char[size];
11858 if (!block)
11859 return NULL;
11860
11861 stream.Read(block, size);
11862
11863 return block;
11864}
11865
11866unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11867{
11868 wxFileInputStream stream(filename);
a1b806b9 11869 if (!stream.IsOk())
5d7836c4
JS
11870 return NULL;
11871
11872 return ReadBlock(stream, size);
11873}
11874
11875// Write memory block to stream
11876bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11877{
11878 stream.Write((void*) block, size);
11879 return stream.IsOk();
11880
11881}
11882
11883// Write memory block to file
11884bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11885{
11886 wxFileOutputStream outStream(filename);
a1b806b9 11887 if (!outStream.IsOk())
7fe8059f 11888 return false;
5d7836c4
JS
11889
11890 return WriteBlock(outStream, block, size);
11891}
11892
d2d0adc7
JS
11893// Gets the extension for the block's type
11894wxString wxRichTextImageBlock::GetExtension() const
11895{
11896 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11897 if (handler)
11898 return handler->GetExtension();
11899 else
11900 return wxEmptyString;
11901}
11902
0ca07313
JS
11903#if wxUSE_DATAOBJ
11904
11905/*!
11906 * The data object for a wxRichTextBuffer
11907 */
11908
11909const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11910
11911wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11912{
11913 m_richTextBuffer = richTextBuffer;
11914
11915 // this string should uniquely identify our format, but is otherwise
11916 // arbitrary
11917 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11918
11919 SetFormat(m_formatRichTextBuffer);
11920}
11921
11922wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11923{
11924 delete m_richTextBuffer;
11925}
11926
11927// after a call to this function, the richTextBuffer is owned by the caller and it
11928// is responsible for deleting it!
11929wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11930{
11931 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11932 m_richTextBuffer = NULL;
11933
11934 return richTextBuffer;
11935}
11936
11937wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11938{
11939 return m_formatRichTextBuffer;
11940}
11941
11942size_t wxRichTextBufferDataObject::GetDataSize() const
11943{
11944 if (!m_richTextBuffer)
11945 return 0;
11946
11947 wxString bufXML;
11948
11949 {
11950 wxStringOutputStream stream(& bufXML);
11951 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11952 {
11953 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11954 return 0;
11955 }
11956 }
11957
11958#if wxUSE_UNICODE
11959 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11960 return strlen(buffer) + 1;
11961#else
11962 return bufXML.Length()+1;
11963#endif
11964}
11965
11966bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11967{
11968 if (!pBuf || !m_richTextBuffer)
11969 return false;
11970
11971 wxString bufXML;
11972
11973 {
11974 wxStringOutputStream stream(& bufXML);
11975 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11976 {
11977 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11978 return 0;
11979 }
11980 }
11981
11982#if wxUSE_UNICODE
11983 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11984 size_t len = strlen(buffer);
11985 memcpy((char*) pBuf, (const char*) buffer, len);
11986 ((char*) pBuf)[len] = 0;
11987#else
11988 size_t len = bufXML.Length();
11989 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11990 ((char*) pBuf)[len] = 0;
11991#endif
11992
11993 return true;
11994}
11995
11996bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
11997{
5276b0a5 11998 wxDELETE(m_richTextBuffer);
0ca07313
JS
11999
12000 wxString bufXML((const char*) buf, wxConvUTF8);
12001
12002 m_richTextBuffer = new wxRichTextBuffer;
12003
12004 wxStringInputStream stream(bufXML);
12005 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
12006 {
12007 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12008
5276b0a5 12009 wxDELETE(m_richTextBuffer);
0ca07313
JS
12010
12011 return false;
12012 }
12013 return true;
12014}
12015
12016#endif
12017 // wxUSE_DATAOBJ
12018
44cc96a8
JS
12019
12020/*
12021 * wxRichTextFontTable
12022 * Manages quick access to a pool of fonts for rendering rich text
12023 */
12024
d65381ac 12025WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
12026
12027class wxRichTextFontTableData: public wxObjectRefData
12028{
12029public:
12030 wxRichTextFontTableData() {}
12031
32423dd8 12032 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
44cc96a8
JS
12033
12034 wxRichTextFontTableHashMap m_hashMap;
12035};
12036
32423dd8 12037wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
44cc96a8
JS
12038{
12039 wxString facename(fontSpec.GetFontFaceName());
44cc96a8 12040
32423dd8
JS
12041 int fontSize = fontSpec.GetFontSize();
12042 if (fontScale != 1.0)
12043 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12044
12045 wxString units;
12046 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12047 units = wxT("px");
12048 else
12049 units = wxT("pt");
12050 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12051 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12052 facename.c_str(), (int) fontSpec.GetFontEncoding());
12053
12054 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
44cc96a8
JS
12055 if ( entry == m_hashMap.end() )
12056 {
32423dd8
JS
12057 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12058 {
b42e4b3e 12059 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
32423dd8
JS
12060 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12061 font.SetStrikethrough(true);
12062 m_hashMap[spec] = font;
12063 return font;
12064 }
12065 else
12066 {
5a0e33af 12067 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
32423dd8
JS
12068 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12069 font.SetStrikethrough(true);
12070
12071 m_hashMap[spec] = font;
12072 return font;
12073 }
44cc96a8
JS
12074 }
12075 else
12076 {
12077 return entry->second;
12078 }
12079}
12080
12081IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12082
12083wxRichTextFontTable::wxRichTextFontTable()
12084{
12085 m_refData = new wxRichTextFontTableData;
32423dd8 12086 m_fontScale = 1.0;
44cc96a8
JS
12087}
12088
12089wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 12090 : wxObject()
44cc96a8
JS
12091{
12092 (*this) = table;
12093}
12094
12095wxRichTextFontTable::~wxRichTextFontTable()
12096{
12097 UnRef();
12098}
12099
12100bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12101{
12102 return (m_refData == table.m_refData);
12103}
12104
12105void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12106{
12107 Ref(table);
32423dd8 12108 m_fontScale = table.m_fontScale;
44cc96a8
JS
12109}
12110
24777478 12111wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
12112{
12113 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12114 if (data)
32423dd8 12115 return data->FindFont(fontSpec, m_fontScale);
44cc96a8
JS
12116 else
12117 return wxFont();
12118}
12119
12120void wxRichTextFontTable::Clear()
12121{
12122 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12123 if (data)
12124 data->m_hashMap.clear();
12125}
12126
32423dd8
JS
12127void wxRichTextFontTable::SetFontScale(double fontScale)
12128{
12129 if (fontScale != m_fontScale)
12130 Clear();
12131 m_fontScale = fontScale;
12132}
12133
24777478
JS
12134// wxTextBoxAttr
12135
24777478
JS
12136void wxTextBoxAttr::Reset()
12137{
12138 m_flags = 0;
603f702b
JS
12139 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12140 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12141 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12142 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 12143 m_boxStyleName = wxEmptyString;
bec80f4f 12144
24777478
JS
12145 m_margins.Reset();
12146 m_padding.Reset();
12147 m_position.Reset();
12148
603f702b 12149 m_size.Reset();
303f0be7
JS
12150 m_minSize.Reset();
12151 m_maxSize.Reset();
24777478
JS
12152
12153 m_border.Reset();
12154 m_outline.Reset();
12155}
12156
12157// Equality test
12158bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12159{
12160 return (
12161 m_flags == attr.m_flags &&
12162 m_floatMode == attr.m_floatMode &&
12163 m_clearMode == attr.m_clearMode &&
12164 m_collapseMode == attr.m_collapseMode &&
603f702b 12165 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 12166
24777478
JS
12167 m_margins == attr.m_margins &&
12168 m_padding == attr.m_padding &&
12169 m_position == attr.m_position &&
12170
603f702b 12171 m_size == attr.m_size &&
303f0be7
JS
12172 m_minSize == attr.m_minSize &&
12173 m_maxSize == attr.m_maxSize &&
24777478
JS
12174
12175 m_border == attr.m_border &&
2f987d83
JS
12176 m_outline == attr.m_outline &&
12177
12178 m_boxStyleName == attr.m_boxStyleName
24777478
JS
12179 );
12180}
12181
12182// Partial equality test
32423dd8 12183bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
24777478 12184{
32423dd8
JS
12185 if (!weakTest &&
12186 ((!HasFloatMode() && attr.HasFloatMode()) ||
12187 (!HasClearMode() && attr.HasClearMode()) ||
12188 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12189 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12190 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12191 {
12192 return false;
12193 }
24777478
JS
12194 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12195 return false;
12196
12197 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12198 return false;
12199
12200 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12201 return false;
12202
603f702b
JS
12203 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12204 return false;
12205
2f987d83
JS
12206 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12207 return false;
12208
24777478
JS
12209 // Position
12210
32423dd8 12211 if (!m_position.EqPartial(attr.m_position, weakTest))
24777478
JS
12212 return false;
12213
303f0be7
JS
12214 // Size
12215
32423dd8 12216 if (!m_size.EqPartial(attr.m_size, weakTest))
303f0be7 12217 return false;
32423dd8 12218 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
303f0be7 12219 return false;
32423dd8 12220 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
303f0be7
JS
12221 return false;
12222
24777478
JS
12223 // Margins
12224
32423dd8 12225 if (!m_margins.EqPartial(attr.m_margins, weakTest))
24777478
JS
12226 return false;
12227
12228 // Padding
12229
32423dd8 12230 if (!m_padding.EqPartial(attr.m_padding, weakTest))
24777478
JS
12231 return false;
12232
12233 // Border
12234
32423dd8 12235 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
24777478
JS
12236 return false;
12237
12238 // Outline
12239
32423dd8 12240 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
24777478
JS
12241 return false;
12242
12243 return true;
12244}
12245
12246// Merges the given attributes. If compareWith
12247// is non-NULL, then it will be used to mask out those attributes that are the same in style
12248// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12249bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12250{
12251 if (attr.HasFloatMode())
12252 {
12253 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12254 SetFloatMode(attr.GetFloatMode());
12255 }
12256
12257 if (attr.HasClearMode())
12258 {
12259 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12260 SetClearMode(attr.GetClearMode());
12261 }
12262
12263 if (attr.HasCollapseBorders())
12264 {
12265 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
12266 SetCollapseBorders(attr.GetCollapseBorders());
12267 }
12268
12269 if (attr.HasVerticalAlignment())
12270 {
12271 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12272 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 12273 }
bec80f4f 12274
2f987d83
JS
12275 if (attr.HasBoxStyleName())
12276 {
12277 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12278 SetBoxStyleName(attr.GetBoxStyleName());
12279 }
12280
bec80f4f
JS
12281 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12282 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12283 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 12284
603f702b 12285 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
12286 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12287 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 12288
bec80f4f
JS
12289 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12290 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
12291
12292 return true;
12293}
12294
12295// Remove specified attributes from this object
12296bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12297{
12298 if (attr.HasFloatMode())
12299 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12300
12301 if (attr.HasClearMode())
12302 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12303
12304 if (attr.HasCollapseBorders())
12305 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12306
603f702b
JS
12307 if (attr.HasVerticalAlignment())
12308 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12309
2f987d83
JS
12310 if (attr.HasBoxStyleName())
12311 {
12312 SetBoxStyleName(wxEmptyString);
12313 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12314 }
12315
24777478
JS
12316 m_margins.RemoveStyle(attr.m_margins);
12317 m_padding.RemoveStyle(attr.m_padding);
12318 m_position.RemoveStyle(attr.m_position);
12319
603f702b 12320 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
12321 m_minSize.RemoveStyle(attr.m_minSize);
12322 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
12323
12324 m_border.RemoveStyle(attr.m_border);
12325 m_outline.RemoveStyle(attr.m_outline);
12326
12327 return true;
12328}
12329
12330// Collects the attributes that are common to a range of content, building up a note of
12331// which attributes are absent in some objects and which clash in some objects.
12332void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12333{
12334 if (attr.HasFloatMode())
12335 {
12336 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12337 {
12338 if (HasFloatMode())
12339 {
12340 if (GetFloatMode() != attr.GetFloatMode())
12341 {
12342 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12343 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12344 }
12345 }
12346 else
12347 SetFloatMode(attr.GetFloatMode());
12348 }
12349 }
12350 else
12351 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 12352
24777478
JS
12353 if (attr.HasClearMode())
12354 {
12355 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12356 {
12357 if (HasClearMode())
12358 {
12359 if (GetClearMode() != attr.GetClearMode())
12360 {
12361 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12362 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12363 }
12364 }
12365 else
12366 SetClearMode(attr.GetClearMode());
12367 }
12368 }
12369 else
12370 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12371
12372 if (attr.HasCollapseBorders())
12373 {
12374 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12375 {
12376 if (HasCollapseBorders())
12377 {
12378 if (GetCollapseBorders() != attr.GetCollapseBorders())
12379 {
12380 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12381 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12382 }
12383 }
12384 else
12385 SetCollapseBorders(attr.GetCollapseBorders());
12386 }
12387 }
12388 else
12389 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 12390
603f702b
JS
12391 if (attr.HasVerticalAlignment())
12392 {
12393 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12394 {
12395 if (HasVerticalAlignment())
12396 {
12397 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12398 {
12399 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12400 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12401 }
12402 }
12403 else
12404 SetVerticalAlignment(attr.GetVerticalAlignment());
12405 }
12406 }
12407 else
12408 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12409
2f987d83
JS
12410 if (attr.HasBoxStyleName())
12411 {
12412 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12413 {
12414 if (HasBoxStyleName())
12415 {
12416 if (GetBoxStyleName() != attr.GetBoxStyleName())
12417 {
12418 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12419 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12420 }
12421 }
12422 else
12423 SetBoxStyleName(attr.GetBoxStyleName());
12424 }
12425 }
12426 else
12427 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12428
24777478
JS
12429 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12430 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12431 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12432
603f702b 12433 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
12434 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12435 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
12436
12437 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12438 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12439}
12440
eb3d8a33
JS
12441bool wxTextBoxAttr::IsDefault() const
12442{
12443 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 12444 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
12445 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12446}
12447
24777478
JS
12448// wxRichTextAttr
12449
12450void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12451{
bec80f4f
JS
12452 wxTextAttr::Copy(attr);
12453
24777478
JS
12454 m_textBoxAttr = attr.m_textBoxAttr;
12455}
12456
12457bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12458{
12459 if (!(wxTextAttr::operator==(attr)))
12460 return false;
bec80f4f 12461
24777478
JS
12462 return (m_textBoxAttr == attr.m_textBoxAttr);
12463}
12464
32423dd8
JS
12465// Partial equality test
12466bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
24777478 12467{
32423dd8 12468 if (!(wxTextAttr::EqPartial(attr, weakTest)))
24777478 12469 return false;
bec80f4f 12470
32423dd8 12471 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
24777478
JS
12472}
12473
12474// Merges the given attributes. If compareWith
12475// is non-NULL, then it will be used to mask out those attributes that are the same in style
12476// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12477bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12478{
12479 wxTextAttr::Apply(style, compareWith);
12480
12481 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12482}
12483
12484// Remove specified attributes from this object
12485bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12486{
12487 wxTextAttr::RemoveStyle(*this, attr);
12488
12489 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12490}
12491
12492// Collects the attributes that are common to a range of content, building up a note of
12493// which attributes are absent in some objects and which clash in some objects.
12494void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12495{
12496 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 12497
24777478
JS
12498 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12499}
12500
12501// Partial equality test
32423dd8 12502bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
24777478 12503{
32423dd8
JS
12504 if (!weakTest &&
12505 ((!HasStyle() && border.HasStyle()) ||
12506 (!HasColour() && border.HasColour()) ||
12507 (!HasWidth() && border.HasWidth())))
12508 {
12509 return false;
12510 }
12511
12512 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
24777478
JS
12513 return false;
12514
32423dd8 12515 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
24777478
JS
12516 return false;
12517
32423dd8 12518 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
24777478
JS
12519 return false;
12520
12521 return true;
12522}
12523
12524// Apply border to 'this', but not if the same as compareWith
bec80f4f 12525bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
12526{
12527 if (border.HasStyle())
12528 {
12529 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12530 SetStyle(border.GetStyle());
12531 }
12532 if (border.HasColour())
12533 {
12534 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12535 SetColour(border.GetColourLong());
12536 }
12537 if (border.HasWidth())
12538 {
12539 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12540 SetWidth(border.GetWidth());
12541 }
12542
12543 return true;
12544}
12545
12546// Remove specified attributes from this object
bec80f4f 12547bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
12548{
12549 if (attr.HasStyle() && HasStyle())
12550 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12551 if (attr.HasColour() && HasColour())
12552 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12553 if (attr.HasWidth() && HasWidth())
12554 m_borderWidth.Reset();
12555
12556 return true;
12557}
12558
12559// Collects the attributes that are common to a range of content, building up a note of
12560// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12561void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
12562{
12563 if (attr.HasStyle())
12564 {
12565 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12566 {
12567 if (HasStyle())
12568 {
12569 if (GetStyle() != attr.GetStyle())
12570 {
12571 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12572 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12573 }
12574 }
12575 else
12576 SetStyle(attr.GetStyle());
12577 }
12578 }
12579 else
12580 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12581
12582 if (attr.HasColour())
12583 {
12584 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12585 {
12586 if (HasColour())
12587 {
12588 if (GetColour() != attr.GetColour())
12589 {
12590 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12591 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12592 }
12593 }
12594 else
12595 SetColour(attr.GetColourLong());
12596 }
12597 }
12598 else
12599 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 12600
24777478
JS
12601 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12602}
12603
12604// Partial equality test
32423dd8 12605bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
24777478 12606{
32423dd8
JS
12607 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12608 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
24777478
JS
12609}
12610
12611// Apply border to 'this', but not if the same as compareWith
bec80f4f 12612bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 12613{
bec80f4f
JS
12614 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12615 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12616 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12617 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
12618 return true;
12619}
12620
12621// Remove specified attributes from this object
bec80f4f 12622bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
12623{
12624 m_left.RemoveStyle(attr.m_left);
12625 m_right.RemoveStyle(attr.m_right);
12626 m_top.RemoveStyle(attr.m_top);
12627 m_bottom.RemoveStyle(attr.m_bottom);
12628 return true;
12629}
12630
12631// Collects the attributes that are common to a range of content, building up a note of
12632// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12633void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
12634{
12635 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12636 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12637 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12638 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12639}
12640
12641// Set style of all borders
bec80f4f 12642void wxTextAttrBorders::SetStyle(int style)
24777478
JS
12643{
12644 m_left.SetStyle(style);
12645 m_right.SetStyle(style);
12646 m_top.SetStyle(style);
12647 m_bottom.SetStyle(style);
12648}
12649
12650// Set colour of all borders
bec80f4f 12651void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
12652{
12653 m_left.SetColour(colour);
12654 m_right.SetColour(colour);
12655 m_top.SetColour(colour);
12656 m_bottom.SetColour(colour);
12657}
12658
bec80f4f 12659void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
12660{
12661 m_left.SetColour(colour);
12662 m_right.SetColour(colour);
12663 m_top.SetColour(colour);
12664 m_bottom.SetColour(colour);
12665}
12666
12667// Set width of all borders
bec80f4f 12668void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
12669{
12670 m_left.SetWidth(width);
12671 m_right.SetWidth(width);
12672 m_top.SetWidth(width);
12673 m_bottom.SetWidth(width);
12674}
12675
12676// Partial equality test
32423dd8 12677bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
24777478 12678{
32423dd8
JS
12679 if (!weakTest && !IsValid() && dim.IsValid())
12680 return false;
12681
603f702b 12682 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
12683 return false;
12684 else
12685 return true;
12686}
12687
12688bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12689{
603f702b 12690 if (dim.IsValid())
24777478
JS
12691 {
12692 if (!(compareWith && dim == (*compareWith)))
12693 (*this) = dim;
12694 }
12695
12696 return true;
12697}
12698
12699// Collects the attributes that are common to a range of content, building up a note of
12700// which attributes are absent in some objects and which clash in some objects.
12701void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12702{
603f702b 12703 if (attr.IsValid())
24777478 12704 {
603f702b 12705 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 12706 {
603f702b 12707 if (IsValid())
24777478
JS
12708 {
12709 if (!((*this) == attr))
12710 {
603f702b
JS
12711 clashingAttr.SetValid(true);
12712 SetValid(false);
24777478
JS
12713 }
12714 }
12715 else
12716 (*this) = attr;
12717 }
12718 }
12719 else
603f702b 12720 absentAttr.SetValid(true);
24777478
JS
12721}
12722
8995db52
JS
12723wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12724{
12725 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12726}
12727
12728wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12729{
12730 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12731}
12732
bec80f4f
JS
12733int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12734{
12735 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12736}
12737
12738int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12739{
12740 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12741}
12742
12743int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12744{
12745 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12746 return ConvertTenthsMMToPixels(dim.GetValue());
12747 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12748 return dim.GetValue();
12749 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12750 {
12751 wxASSERT(m_parentSize != wxDefaultSize);
12752 if (direction == wxHORIZONTAL)
12753 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12754 else
12755 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12756 }
12757 else
12758 {
12759 wxASSERT(false);
12760 return 0;
12761 }
12762}
12763
12764int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12765{
12766 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12767 return dim.GetValue();
12768 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12769 return ConvertPixelsToTenthsMM(dim.GetValue());
12770 else
12771 {
12772 wxASSERT(false);
12773 return 0;
12774 }
12775}
12776
24777478 12777// Partial equality test
32423dd8 12778bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
24777478 12779{
32423dd8 12780 if (!m_left.EqPartial(dims.m_left, weakTest))
24777478
JS
12781 return false;
12782
32423dd8 12783 if (!m_right.EqPartial(dims.m_right, weakTest))
24777478
JS
12784 return false;
12785
32423dd8 12786 if (!m_top.EqPartial(dims.m_top, weakTest))
24777478
JS
12787 return false;
12788
32423dd8 12789 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
24777478
JS
12790 return false;
12791
12792 return true;
12793}
12794
12795// Apply border to 'this', but not if the same as compareWith
bec80f4f 12796bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
12797{
12798 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12799 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12800 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12801 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12802
12803 return true;
12804}
12805
12806// Remove specified attributes from this object
bec80f4f 12807bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 12808{
603f702b 12809 if (attr.m_left.IsValid())
24777478 12810 m_left.Reset();
603f702b 12811 if (attr.m_right.IsValid())
24777478 12812 m_right.Reset();
603f702b 12813 if (attr.m_top.IsValid())
24777478 12814 m_top.Reset();
603f702b 12815 if (attr.m_bottom.IsValid())
24777478
JS
12816 m_bottom.Reset();
12817
12818 return true;
12819}
12820
12821// Collects the attributes that are common to a range of content, building up a note of
12822// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12823void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
12824{
12825 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12826 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12827 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12828 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12829}
12830
603f702b 12831// Partial equality test
32423dd8 12832bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
603f702b 12833{
32423dd8 12834 if (!m_width.EqPartial(size.m_width, weakTest))
603f702b
JS
12835 return false;
12836
32423dd8 12837 if (!m_height.EqPartial(size.m_height, weakTest))
603f702b
JS
12838 return false;
12839
12840 return true;
12841}
12842
12843// Apply border to 'this', but not if the same as compareWith
12844bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12845{
12846 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12847 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12848
12849 return true;
12850}
12851
12852// Remove specified attributes from this object
12853bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12854{
12855 if (attr.m_width.IsValid())
12856 m_width.Reset();
12857 if (attr.m_height.IsValid())
12858 m_height.Reset();
12859
12860 return true;
12861}
12862
12863// Collects the attributes that are common to a range of content, building up a note of
12864// which attributes are absent in some objects and which clash in some objects.
12865void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12866{
12867 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12868 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12869}
12870
24777478
JS
12871// Collects the attributes that are common to a range of content, building up a note of
12872// which attributes are absent in some objects and which clash in some objects.
12873void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12874{
12875 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12876 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12877
12878 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12879
c4168888
JS
12880 // If different font size units are being used, this is a clash.
12881 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
24777478 12882 {
c4168888
JS
12883 currentStyle.SetFontSize(0);
12884 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12885 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12886 }
12887 else
12888 {
12889 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
340ef5c5 12890 {
c4168888 12891 if (currentStyle.HasFontPointSize())
24777478 12892 {
c4168888 12893 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478 12894 {
c4168888
JS
12895 // Clash of attr - mark as such
12896 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12897 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
12898 }
12899 }
c4168888
JS
12900 else
12901 currentStyle.SetFontSize(attr.GetFontSize());
12902 }
12903 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
12904 {
12905 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12906 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
12907 }
12908
c4168888 12909 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
24777478 12910 {
c4168888 12911 if (currentStyle.HasFontPixelSize())
24777478 12912 {
c4168888 12913 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478
JS
12914 {
12915 // Clash of attr - mark as such
c4168888
JS
12916 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12917 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
24777478
JS
12918 }
12919 }
12920 else
c4168888 12921 currentStyle.SetFontPixelSize(attr.GetFontSize());
24777478 12922 }
c4168888
JS
12923 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
12924 {
12925 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12926 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12927 }
12928 }
24777478 12929
c4168888
JS
12930 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12931 {
12932 if (currentStyle.HasFontItalic())
24777478 12933 {
c4168888 12934 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
24777478 12935 {
c4168888
JS
12936 // Clash of attr - mark as such
12937 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12938 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
24777478 12939 }
24777478 12940 }
c4168888
JS
12941 else
12942 currentStyle.SetFontStyle(attr.GetFontStyle());
12943 }
12944 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
12945 {
12946 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12947 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12948 }
24777478 12949
c4168888
JS
12950 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12951 {
12952 if (currentStyle.HasFontFamily())
24777478 12953 {
c4168888 12954 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
24777478 12955 {
c4168888
JS
12956 // Clash of attr - mark as such
12957 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12958 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
24777478 12959 }
24777478 12960 }
c4168888
JS
12961 else
12962 currentStyle.SetFontFamily(attr.GetFontFamily());
12963 }
12964 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
12965 {
12966 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12967 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12968 }
24777478 12969
c4168888
JS
12970 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12971 {
12972 if (currentStyle.HasFontWeight())
24777478 12973 {
c4168888 12974 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
24777478 12975 {
c4168888
JS
12976 // Clash of attr - mark as such
12977 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12978 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12979 }
12980 }
12981 else
12982 currentStyle.SetFontWeight(attr.GetFontWeight());
12983 }
12984 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
12985 {
12986 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12987 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12988 }
24777478 12989
c4168888
JS
12990 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12991 {
12992 if (currentStyle.HasFontFaceName())
12993 {
12994 wxString faceName1(currentStyle.GetFontFaceName());
12995 wxString faceName2(attr.GetFontFaceName());
12996
12997 if (faceName1 != faceName2)
12998 {
12999 // Clash of attr - mark as such
13000 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13001 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
24777478 13002 }
24777478 13003 }
c4168888
JS
13004 else
13005 currentStyle.SetFontFaceName(attr.GetFontFaceName());
13006 }
13007 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
13008 {
13009 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13010 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13011 }
24777478 13012
c4168888
JS
13013 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
13014 {
13015 if (currentStyle.HasFontUnderlined())
24777478 13016 {
c4168888 13017 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
24777478 13018 {
c4168888
JS
13019 // Clash of attr - mark as such
13020 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13021 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
24777478 13022 }
24777478 13023 }
c4168888
JS
13024 else
13025 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
13026 }
13027 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
13028 {
13029 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13030 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13031 }
32423dd8 13032
c4168888
JS
13033 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
13034 {
13035 if (currentStyle.HasFontStrikethrough())
32423dd8 13036 {
c4168888 13037 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
32423dd8 13038 {
c4168888
JS
13039 // Clash of attr - mark as such
13040 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13041 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
32423dd8 13042 }
32423dd8 13043 }
c4168888
JS
13044 else
13045 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13046 }
13047 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13048 {
13049 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13050 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
24777478
JS
13051 }
13052
13053 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13054 {
13055 if (currentStyle.HasTextColour())
13056 {
13057 if (currentStyle.GetTextColour() != attr.GetTextColour())
13058 {
13059 // Clash of attr - mark as such
13060 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13061 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13062 }
13063 }
13064 else
13065 currentStyle.SetTextColour(attr.GetTextColour());
13066 }
c4168888
JS
13067 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13068 {
13069 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13070 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13071 }
24777478
JS
13072
13073 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13074 {
13075 if (currentStyle.HasBackgroundColour())
13076 {
13077 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13078 {
13079 // Clash of attr - mark as such
13080 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13081 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13082 }
13083 }
13084 else
13085 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13086 }
c4168888
JS
13087 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13088 {
13089 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13090 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13091 }
24777478
JS
13092
13093 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13094 {
13095 if (currentStyle.HasAlignment())
13096 {
13097 if (currentStyle.GetAlignment() != attr.GetAlignment())
13098 {
13099 // Clash of attr - mark as such
13100 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13101 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13102 }
13103 }
13104 else
13105 currentStyle.SetAlignment(attr.GetAlignment());
13106 }
c4168888
JS
13107 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13108 {
13109 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13110 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13111 }
24777478
JS
13112
13113 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13114 {
13115 if (currentStyle.HasTabs())
13116 {
13117 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13118 {
13119 // Clash of attr - mark as such
13120 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13121 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13122 }
13123 }
13124 else
13125 currentStyle.SetTabs(attr.GetTabs());
13126 }
c4168888
JS
13127 else if (!attr.HasTabs() && currentStyle.HasTabs())
13128 {
13129 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13130 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13131 }
24777478
JS
13132
13133 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13134 {
13135 if (currentStyle.HasLeftIndent())
13136 {
13137 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13138 {
13139 // Clash of attr - mark as such
13140 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13141 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13142 }
13143 }
13144 else
13145 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13146 }
c4168888
JS
13147 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13148 {
13149 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13150 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13151 }
24777478
JS
13152
13153 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13154 {
13155 if (currentStyle.HasRightIndent())
13156 {
13157 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13158 {
13159 // Clash of attr - mark as such
13160 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13161 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13162 }
13163 }
13164 else
13165 currentStyle.SetRightIndent(attr.GetRightIndent());
13166 }
c4168888
JS
13167 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13168 {
13169 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13170 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13171 }
24777478
JS
13172
13173 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13174 {
13175 if (currentStyle.HasParagraphSpacingAfter())
13176 {
13177 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13178 {
13179 // Clash of attr - mark as such
13180 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13181 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13182 }
13183 }
13184 else
13185 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13186 }
c4168888
JS
13187 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13188 {
13189 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13190 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13191 }
24777478
JS
13192
13193 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13194 {
13195 if (currentStyle.HasParagraphSpacingBefore())
13196 {
13197 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13198 {
13199 // Clash of attr - mark as such
13200 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13201 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13202 }
13203 }
13204 else
13205 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13206 }
c4168888
JS
13207 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13208 {
13209 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13210 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13211 }
24777478
JS
13212
13213 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13214 {
13215 if (currentStyle.HasLineSpacing())
13216 {
13217 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13218 {
13219 // Clash of attr - mark as such
13220 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13221 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13222 }
13223 }
13224 else
13225 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13226 }
c4168888
JS
13227 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13228 {
13229 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13230 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13231 }
24777478
JS
13232
13233 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13234 {
13235 if (currentStyle.HasCharacterStyleName())
13236 {
13237 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13238 {
13239 // Clash of attr - mark as such
13240 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13241 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13242 }
13243 }
13244 else
13245 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13246 }
c4168888
JS
13247 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13248 {
13249 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13250 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13251 }
24777478
JS
13252
13253 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13254 {
13255 if (currentStyle.HasParagraphStyleName())
13256 {
13257 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13258 {
13259 // Clash of attr - mark as such
13260 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13261 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13262 }
13263 }
13264 else
13265 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13266 }
c4168888
JS
13267 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13268 {
13269 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13270 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13271 }
24777478
JS
13272
13273 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13274 {
13275 if (currentStyle.HasListStyleName())
13276 {
13277 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13278 {
13279 // Clash of attr - mark as such
13280 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13281 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13282 }
13283 }
13284 else
13285 currentStyle.SetListStyleName(attr.GetListStyleName());
13286 }
c4168888
JS
13287 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13288 {
13289 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13290 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13291 }
24777478
JS
13292
13293 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13294 {
13295 if (currentStyle.HasBulletStyle())
13296 {
13297 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13298 {
13299 // Clash of attr - mark as such
13300 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13301 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13302 }
13303 }
13304 else
13305 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13306 }
c4168888
JS
13307 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13308 {
13309 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13310 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13311 }
24777478
JS
13312
13313 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13314 {
13315 if (currentStyle.HasBulletNumber())
13316 {
13317 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13318 {
13319 // Clash of attr - mark as such
13320 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13321 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13322 }
13323 }
13324 else
13325 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13326 }
c4168888
JS
13327 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13328 {
13329 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13330 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13331 }
24777478
JS
13332
13333 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13334 {
13335 if (currentStyle.HasBulletText())
13336 {
13337 if (currentStyle.GetBulletText() != attr.GetBulletText())
13338 {
13339 // Clash of attr - mark as such
13340 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13341 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13342 }
13343 }
13344 else
13345 {
13346 currentStyle.SetBulletText(attr.GetBulletText());
13347 currentStyle.SetBulletFont(attr.GetBulletFont());
13348 }
13349 }
c4168888
JS
13350 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13351 {
13352 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13353 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13354 }
24777478
JS
13355
13356 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13357 {
13358 if (currentStyle.HasBulletName())
13359 {
13360 if (currentStyle.GetBulletName() != attr.GetBulletName())
13361 {
13362 // Clash of attr - mark as such
13363 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13364 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13365 }
13366 }
13367 else
13368 {
13369 currentStyle.SetBulletName(attr.GetBulletName());
13370 }
13371 }
c4168888
JS
13372 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13373 {
13374 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13375 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13376 }
24777478
JS
13377
13378 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13379 {
13380 if (currentStyle.HasURL())
13381 {
13382 if (currentStyle.GetURL() != attr.GetURL())
13383 {
13384 // Clash of attr - mark as such
13385 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13386 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13387 }
13388 }
13389 else
13390 {
13391 currentStyle.SetURL(attr.GetURL());
13392 }
13393 }
c4168888
JS
13394 else if (!attr.HasURL() && currentStyle.HasURL())
13395 {
13396 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13397 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13398 }
24777478
JS
13399
13400 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13401 {
13402 if (currentStyle.HasTextEffects())
13403 {
13404 // We need to find the bits in the new attr that are different:
13405 // just look at those bits that are specified by the new attr.
13406
13407 // We need to remove the bits and flags that are not common between current attr
13408 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13409 // previous styles.
13410
13411 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13412 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13413
13414 if (currentRelevantTextEffects != newRelevantTextEffects)
13415 {
13416 // Find the text effects that were different, using XOR
13417 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13418
13419 // Clash of attr - mark as such
13420 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13421 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13422 }
13423 }
13424 else
13425 {
13426 currentStyle.SetTextEffects(attr.GetTextEffects());
13427 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13428 }
13429
13430 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13431 // that we've looked at so far
13432 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13433 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13434
13435 if (currentStyle.GetTextEffectFlags() == 0)
13436 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13437 }
c4168888
JS
13438 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13439 {
13440 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13441 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13442 }
24777478
JS
13443
13444 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13445 {
13446 if (currentStyle.HasOutlineLevel())
13447 {
13448 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13449 {
13450 // Clash of attr - mark as such
13451 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13452 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13453 }
13454 }
13455 else
13456 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13457 }
c4168888
JS
13458 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13459 {
13460 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13461 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13462 }
24777478
JS
13463}
13464
bec80f4f
JS
13465WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13466
f7667b84
JS
13467// JACS 2013-01-27
13468WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13469
bec80f4f
JS
13470IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13471
13472bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13473{
13474 if (m_properties.GetCount() != props.GetCount())
13475 return false;
13476
13477 size_t i;
13478 for (i = 0; i < m_properties.GetCount(); i++)
13479 {
13480 const wxVariant& var1 = m_properties[i];
13481 int idx = props.Find(var1.GetName());
13482 if (idx == -1)
13483 return false;
13484 const wxVariant& var2 = props.m_properties[idx];
13485 if (!(var1 == var2))
13486 return false;
13487 }
13488
13489 return true;
13490}
13491
13492wxArrayString wxRichTextProperties::GetPropertyNames() const
13493{
13494 wxArrayString arr;
13495 size_t i;
13496 for (i = 0; i < m_properties.GetCount(); i++)
13497 {
13498 arr.Add(m_properties[i].GetName());
13499 }
13500 return arr;
13501}
13502
13503int wxRichTextProperties::Find(const wxString& name) const
13504{
13505 size_t i;
13506 for (i = 0; i < m_properties.GetCount(); i++)
13507 {
13508 if (m_properties[i].GetName() == name)
13509 return (int) i;
13510 }
13511 return -1;
13512}
13513
590a0f8b
JS
13514bool wxRichTextProperties::Remove(const wxString& name)
13515{
13516 int idx = Find(name);
13517 if (idx != -1)
13518 {
13519 m_properties.RemoveAt(idx);
13520 return true;
13521 }
13522 else
13523 return false;
13524}
13525
bec80f4f
JS
13526wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13527{
13528 int idx = Find(name);
13529 if (idx == wxNOT_FOUND)
13530 SetProperty(name, wxString());
13531 idx = Find(name);
13532 if (idx != wxNOT_FOUND)
13533 {
13534 return & (*this)[idx];
13535 }
13536 else
13537 return NULL;
13538}
13539
13540const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13541{
13542 static const wxVariant nullVariant;
13543 int idx = Find(name);
13544 if (idx != -1)
13545 return m_properties[idx];
13546 else
13547 return nullVariant;
13548}
13549
13550wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13551{
13552 return GetProperty(name).GetString();
13553}
13554
13555long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13556{
13557 return GetProperty(name).GetLong();
13558}
13559
13560bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13561{
13562 return GetProperty(name).GetBool();
13563}
13564
13565double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13566{
13567 return GetProperty(name).GetDouble();
13568}
13569
13570void wxRichTextProperties::SetProperty(const wxVariant& variant)
13571{
13572 wxASSERT(!variant.GetName().IsEmpty());
13573
13574 int idx = Find(variant.GetName());
13575
13576 if (idx == -1)
13577 m_properties.Add(variant);
13578 else
13579 m_properties[idx] = variant;
13580}
13581
13582void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13583{
13584 int idx = Find(name);
13585 wxVariant var(variant);
13586 var.SetName(name);
13587
13588 if (idx == -1)
13589 m_properties.Add(var);
13590 else
13591 m_properties[idx] = var;
13592}
13593
13594void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13595{
13596 SetProperty(name, wxVariant(value, name));
13597}
13598
13599void wxRichTextProperties::SetProperty(const wxString& name, long value)
13600{
13601 SetProperty(name, wxVariant(value, name));
13602}
13603
13604void wxRichTextProperties::SetProperty(const wxString& name, double value)
13605{
13606 SetProperty(name, wxVariant(value, name));
13607}
13608
13609void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13610{
13611 SetProperty(name, wxVariant(value, name));
13612}
24777478 13613
590a0f8b
JS
13614void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13615{
13616 size_t i;
13617 for (i = 0; i < properties.GetCount(); i++)
13618 {
13619 wxString name = properties.GetProperties()[i].GetName();
13620 if (HasProperty(name))
13621 Remove(name);
13622 }
13623}
13624
13625void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13626{
13627 size_t i;
13628 for (i = 0; i < properties.GetCount(); i++)
13629 {
13630 SetProperty(properties.GetProperties()[i]);
13631 }
13632}
13633
603f702b
JS
13634wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13635{
13636 if (m_address.GetCount() == 0)
13637 return topLevelContainer;
13638
13639 wxRichTextCompositeObject* p = topLevelContainer;
13640 size_t i = 0;
13641 while (p && i < m_address.GetCount())
13642 {
13643 int pos = m_address[i];
13644 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13645 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13646 return NULL;
13647
13648 wxRichTextObject* p1 = p->GetChild(pos);
13649 if (i == (m_address.GetCount()-1))
13650 return p1;
13651
13652 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13653 i ++;
13654 }
13655 return NULL;
13656}
13657
13658bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13659{
13660 m_address.Clear();
13661
13662 if (topLevelContainer == obj)
13663 return true;
13664
13665 wxRichTextObject* o = obj;
13666 while (o)
13667 {
13668 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13669 if (!p)
13670 return false;
13671
13672 int pos = p->GetChildren().IndexOf(o);
13673 if (pos == -1)
13674 return false;
13675
13676 m_address.Insert(pos, 0);
13677
13678 if (p == topLevelContainer)
13679 return true;
13680
13681 o = p;
13682 }
13683 return false;
13684}
13685
13686// Equality test
13687bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13688{
13689 if (m_container != sel.m_container)
13690 return false;
13691 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13692 return false;
13693 size_t i;
13694 for (i = 0; i < m_ranges.GetCount(); i++)
13695 if (!(m_ranges[i] == sel.m_ranges[i]))
13696 return false;
13697 return true;
13698}
13699
13700// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13701// or none at the level of the object's container.
13702wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13703{
13704 if (IsValid())
13705 {
13706 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13707
13708 if (container == m_container)
13709 return m_ranges;
13710
13711 container = obj->GetContainer();
13712 while (container)
13713 {
13714 if (container->GetParent())
13715 {
13716 // If we found that our object's container is within the range of
13717 // a selection higher up, then assume the whole original object
13718 // is also selected.
13719 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13720 if (parentContainer == m_container)
13721 {
13722 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13723 {
13724 wxRichTextRangeArray ranges;
13725 ranges.Add(obj->GetRange());
13726 return ranges;
13727 }
13728 }
13729
13730 container = parentContainer;
13731 }
13732 else
13733 {
13734 container = NULL;
13735 break;
13736 }
13737 }
13738 }
13739 return wxRichTextRangeArray();
13740}
13741
13742// Is the given position within the selection?
13743bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13744{
13745 if (!IsValid())
13746 return false;
13747 else
13748 {
13749 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13750 return WithinSelection(pos, selectionRanges);
13751 }
13752}
13753
13754// Is the given position within the selection range?
13755bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13756{
13757 size_t i;
13758 for (i = 0; i < ranges.GetCount(); i++)
13759 {
13760 const wxRichTextRange& range = ranges[i];
13761 if (pos >= range.GetStart() && pos <= range.GetEnd())
13762 return true;
13763 }
13764 return false;
13765}
13766
13767// Is the given range completely within the selection range?
13768bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13769{
13770 size_t i;
13771 for (i = 0; i < ranges.GetCount(); i++)
13772 {
13773 const wxRichTextRange& eachRange = ranges[i];
13774 if (range.IsWithin(eachRange))
13775 return true;
13776 }
13777 return false;
13778}
13779
8db2e3ef
JS
13780IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13781IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13782
f7667b84
JS
13783wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
13784{
13785 Init();
13786 m_buffer = buffer;
13787 if (m_buffer && m_buffer->GetRichTextCtrl())
13788 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13789}
13790
8db2e3ef
JS
13791bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13792{
f7667b84
JS
13793 if (!GetVirtualAttributesEnabled())
13794 return false;
13795
8db2e3ef
JS
13796 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13797 while (node)
13798 {
13799 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13800 if (handler->HasVirtualAttributes(obj))
13801 return true;
13802
13803 node = node->GetNext();
13804 }
13805 return false;
13806}
13807
13808wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13809{
13810 wxRichTextAttr attr;
f7667b84
JS
13811 if (!GetVirtualAttributesEnabled())
13812 return attr;
13813
8db2e3ef
JS
13814 // We apply all handlers, so we can may combine several different attributes
13815 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13816 while (node)
13817 {
13818 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13819 if (handler->HasVirtualAttributes(obj))
13820 {
13821 bool success = handler->GetVirtualAttributes(attr, obj);
13822 wxASSERT(success);
aa8f57f4 13823 wxUnusedVar(success);
8db2e3ef
JS
13824 }
13825
13826 node = node->GetNext();
13827 }
13828 return attr;
13829}
13830
13831bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13832{
f7667b84
JS
13833 if (!GetVirtualAttributesEnabled())
13834 return false;
13835
8db2e3ef
JS
13836 if (HasVirtualAttributes(obj))
13837 {
13838 wxRichTextAttr a(GetVirtualAttributes(obj));
13839 attr.Apply(a);
13840 return true;
13841 }
13842 else
13843 return false;
13844}
13845
f7667b84
JS
13846int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
13847{
13848 if (!GetVirtualAttributesEnabled())
13849 return 0;
13850
13851 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13852 while (node)
13853 {
13854 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13855 int count = handler->GetVirtualSubobjectAttributesCount(obj);
13856 if (count > 0)
13857 return count;
13858
13859 node = node->GetNext();
13860 }
13861 return 0;
13862}
13863
13864int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
13865{
13866 if (!GetVirtualAttributesEnabled())
13867 return 0;
13868
13869 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13870 while (node)
13871 {
13872 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13873 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
13874 return positions.GetCount();
13875
13876 node = node->GetNext();
13877 }
13878 return 0;
13879}
13880
13881bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
13882{
13883 if (!GetVirtualAttributesEnabled())
13884 return false;
13885
13886 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13887 while (node)
13888 {
13889 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13890 if (handler->HasVirtualText(obj))
13891 return true;
13892
13893 node = node->GetNext();
13894 }
13895 return false;
13896}
13897
13898bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
13899{
13900 if (!GetVirtualAttributesEnabled())
13901 return false;
13902
13903 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13904 while (node)
13905 {
13906 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13907 if (handler->GetVirtualText(obj, text))
13908 return true;
13909
13910 node = node->GetNext();
13911 }
13912 return false;
13913}
13914
8db2e3ef
JS
13915/// Adds a handler to the end
13916void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13917{
13918 sm_drawingHandlers.Append(handler);
13919}
13920
13921/// Inserts a handler at the front
13922void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13923{
13924 sm_drawingHandlers.Insert( handler );
13925}
13926
13927/// Removes a handler
13928bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13929{
13930 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13931 if (handler)
13932 {
13933 sm_drawingHandlers.DeleteObject(handler);
13934 delete handler;
13935 return true;
13936 }
13937 else
13938 return false;
13939}
13940
13941wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13942{
13943 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13944 while (node)
13945 {
13946 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13947 if (handler->GetName().Lower() == name.Lower()) return handler;
13948
13949 node = node->GetNext();
13950 }
13951 return NULL;
13952}
13953
13954void wxRichTextBuffer::CleanUpDrawingHandlers()
13955{
13956 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13957 while (node)
13958 {
13959 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13960 wxList::compatibility_iterator next = node->GetNext();
13961 delete handler;
13962 node = next;
13963 }
13964
13965 sm_drawingHandlers.Clear();
13966}
603f702b 13967
7c9fdebe
JS
13968void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13969{
13970 sm_fieldTypes[fieldType->GetName()] = fieldType;
13971}
13972
13973bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13974{
13975 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13976 if (it == sm_fieldTypes.end())
13977 return false;
13978 else
13979 {
13980 wxRichTextFieldType* fieldType = it->second;
13981 sm_fieldTypes.erase(it);
13982 delete fieldType;
13983 return true;
13984 }
13985}
13986
13987wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13988{
13989 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13990 if (it == sm_fieldTypes.end())
13991 return NULL;
13992 else
13993 return it->second;
13994}
13995
13996void wxRichTextBuffer::CleanUpFieldTypes()
13997{
13998 wxRichTextFieldTypeHashMap::iterator it;
13999 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
14000 {
14001 wxRichTextFieldType* fieldType = it->second;
14002 delete fieldType;
14003 }
14004
14005 sm_fieldTypes.clear();
14006}
14007
5d7836c4
JS
14008#endif
14009 // wxUSE_RICHTEXT