]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
Move menu messages handling from wxFrame to wxTLW in wxMSW.
[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());
809 wxColour col(attr.GetTop().GetColour());
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.
8db2e3ef 1536bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, 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 }
8db2e3ef 1592 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), 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.
8db2e3ef 2186bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, 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;
8db2e3ef 2245 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
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
8db2e3ef 4722 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & 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
8db2e3ef 4804 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & 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
8db2e3ef 4821 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4f3d5bc0
JS
4822 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4823#else
8db2e3ef 4824 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
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
JS
4892 child->LayoutToBestSize(dc, context, buffer,
4893 attr, child->GetAttributes(), availableRect, parentRect, 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
8db2e3ef 4902 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & 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)
4931 ||
4932 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4933
4934 )
5d7836c4
JS
4935 {
4936 long wrapPosition = 0;
603f702b
JS
4937 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4938 wrapPosition = child->GetRange().GetEnd();
4939 else
5d7836c4
JS
4940
4941 // Find a place to wrap. This may walk back to previous children,
4942 // for example if a word spans several objects.
cdaed652
VZ
4943 // Note: one object must contains only one wxTextAtrr, so the line height will not
4944 // change inside one object. Thus, we can pass the remain line width to the
4945 // FindWrapPosition function.
8db2e3ef 4946 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4947 {
4948 // If the function failed, just cut it off at the end of this child.
4949 wrapPosition = child->GetRange().GetEnd();
4950 }
4951
4952 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4953 if (wrapPosition <= lastCompletedEndPos)
4954 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4955
603f702b
JS
4956 // Line end position shouldn't be the same as the end, or greater.
4957 if (wrapPosition >= GetRange().GetEnd())
a8a15de6 4958 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
603f702b 4959
5d7836c4 4960 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4961
5d7836c4
JS
4962 // Let's find the actual size of the current line now
4963 wxSize actualSize;
4964 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4965
a70eb13e 4966 childDescent = 0;
4ab8a5e2 4967
4f3d5bc0 4968#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4969 if (!child->IsEmpty())
4970 {
4971 // Get height only, then the width using the partial extents
8db2e3ef 4972 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
603f702b
JS
4973 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4974 }
4975 else
4f3d5bc0 4976#endif
8db2e3ef 4977 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
4f3d5bc0 4978
5d7836c4 4979 currentWidth = actualSize.x;
a70eb13e
JS
4980
4981 // The descent for the whole line at this point, is the correct max descent
4982 maxDescent = childDescent;
4983 // Maximum ascent
4984 maxAscent = actualSize.y-childDescent;
4985
4986 // lineHeight is given by the height for the whole line, since it will
4987 // take into account ascend/descend.
4988 lineHeight = actualSize.y;
7fe8059f 4989
07d4142f 4990 if (lineHeight == 0 && buffer)
603f702b 4991 {
07d4142f 4992 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
4993 wxCheckSetFont(dc, font);
4994 lineHeight = dc.GetCharHeight();
4995 }
4996
4997 if (maxDescent == 0)
4998 {
4999 int w, h;
5000 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5001 }
5002
5d7836c4 5003 // Add a new line
1e967276 5004 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 5005
1e967276
JS
5006 // Set relative range so we won't have to change line ranges when paragraphs are moved
5007 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
5008 line->SetPosition(currentPosition);
5009 line->SetSize(wxSize(currentWidth, lineHeight));
5010 line->SetDescent(maxDescent);
5011
603f702b
JS
5012 maxHeight = currentPosition.y + lineHeight;
5013
5d7836c4
JS
5014 // Now move down a line. TODO: add margins, spacing
5015 currentPosition.y += lineHeight;
5016 currentPosition.y += lineSpacing;
5d7836c4 5017 maxDescent = 0;
476a319a 5018 maxAscent = 0;
603f702b
JS
5019 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5020 currentWidth = 0;
7fe8059f 5021
5d7836c4
JS
5022 lineCount ++;
5023
a70eb13e 5024 // TODO: account for zero-length objects
603f702b 5025 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 5026
5d7836c4
JS
5027 lastEndPos = wrapPosition;
5028 lastCompletedEndPos = lastEndPos;
5029
5030 lineHeight = 0;
5031
603f702b
JS
5032 if (wrapPosition < GetRange().GetEnd()-1)
5033 {
5034 // May need to set the node back to a previous one, due to searching back in wrapping
5035 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5036 if (childAfterWrapPosition)
5037 node = m_children.Find(childAfterWrapPosition);
5038 else
5039 node = node->GetNext();
5040 }
5d7836c4
JS
5041 else
5042 node = node->GetNext();
603f702b
JS
5043
5044 // Apply paragraph styles such as alignment to the wrapped line
5045 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
5046 }
5047 else
5048 {
5049 // We still fit, so don't add a line, and keep going
5050 currentWidth += childSize.x;
a70eb13e
JS
5051
5052 if (childDescent == 0)
5053 {
5054 // An object with a zero descend value wants to take up the whole
5055 // height regardless of baseline
5056 lineHeight = wxMax(lineHeight, childSize.y);
5057 }
5058 else
5059 {
5060 maxDescent = wxMax(childDescent, maxDescent);
5061 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5062 }
5063
5064 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5d7836c4 5065
603f702b 5066 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
5067 lastEndPos = child->GetRange().GetEnd();
5068
5069 node = node->GetNext();
5070 }
5071 }
5072
07d4142f 5073 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 5074
1e967276
JS
5075 // Remove remaining unused line objects, if any
5076 ClearUnusedLines(lineCount);
5077
603f702b
JS
5078 // We need to add back the margins etc.
5079 {
5080 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5081 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5082 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5083 SetCachedSize(marginRect.GetSize());
5084 }
5085
5086 // The maximum size is the length of the paragraph stretched out into a line.
5087 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5088 // this size. TODO: take into account line breaks.
5089 {
5090 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 5091 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 5092 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5093 SetMaxSize(marginRect.GetSize());
5094 }
5095
5096 // Find the greatest minimum size. Currently we only look at non-text objects,
5097 // which isn't ideal but it would be slow to find the maximum word width to
5098 // use as the minimum.
5099 {
5100 int minWidth = 0;
5101 node = m_children.GetFirst();
5102 while (node)
5103 {
5104 wxRichTextObject* child = node->GetData();
5105
5106 // If floating, ignore. We already laid out floats.
5107 // Also ignore if empty object, except if we haven't got any
5108 // size yet.
e12b91a3 5109 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
603f702b
JS
5110 {
5111 if (child->GetCachedSize().x > minWidth)
5112 minWidth = child->GetMinSize().x;
5113 }
5114 node = node->GetNext();
5115 }
5d7836c4 5116
603f702b
JS
5117 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5118 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5119 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5120 SetMinSize(marginRect.GetSize());
5121 }
5d7836c4 5122
2f45f554
JS
5123#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5124#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5125 // Use the text extents to calculate the size of each fragment in each line
5126 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5127 while (lineNode)
5128 {
5129 wxRichTextLine* line = lineNode->GetData();
5130 wxRichTextRange lineRange = line->GetAbsoluteRange();
5131
5132 // Loop through objects until we get to the one within range
5133 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5134
5135 while (node2)
5136 {
5137 wxRichTextObject* child = node2->GetData();
5138
affbfa1f 5139 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
5140 {
5141 wxRichTextRange rangeToUse = lineRange;
5142 rangeToUse.LimitTo(child->GetRange());
5143
5144 // Find the size of the child from the text extents, and store in an array
5145 // for drawing later
5146 int left = 0;
5147 if (rangeToUse.GetStart() > GetRange().GetStart())
5148 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5149 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5150 int sz = right - left;
5151 line->GetObjectSizes().Add(sz);
5152 }
5153 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5154 // Can break out of inner loop now since we've passed this line's range
5155 break;
5156
5157 node2 = node2->GetNext();
5158 }
5159
5160 lineNode = lineNode->GetNext();
5161 }
5162#endif
5163#endif
5164
5d7836c4
JS
5165 return true;
5166}
5167
603f702b
JS
5168/// Apply paragraph styles, such as centering, to wrapped lines
5169/// TODO: take into account box attributes, possibly
5170void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5171{
5172 if (!attr.HasAlignment())
5173 return;
5174
5175 wxPoint pos = line->GetPosition();
32423dd8 5176 wxPoint originalPos = pos;
603f702b
JS
5177 wxSize size = line->GetSize();
5178
5179 // centering, right-justification
8db2e3ef 5180 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5181 {
5182 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5183 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5184 line->SetPosition(pos);
5185 }
8db2e3ef 5186 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5187 {
5188 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5189 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5190 line->SetPosition(pos);
5191 }
32423dd8
JS
5192
5193 if (pos != originalPos)
5194 {
5195 wxPoint inc = pos - originalPos;
5196
5197 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5198
5199 while (node)
5200 {
5201 wxRichTextObject* child = node->GetData();
5202 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5203 child->Move(child->GetPosition() + inc);
5204
5205 node = node->GetNext();
5206 }
5207 }
603f702b 5208}
5d7836c4
JS
5209
5210/// Insert text at the given position
5211bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5212{
5213 wxRichTextObject* childToUse = NULL;
09f14108 5214 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5215
5216 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5217 while (node)
5218 {
5219 wxRichTextObject* child = node->GetData();
5220 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5221 {
5222 childToUse = child;
5223 nodeToUse = node;
5224 break;
5225 }
5226
5227 node = node->GetNext();
5228 }
5229
5230 if (childToUse)
5231 {
5232 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5233 if (textObject)
5234 {
5235 int posInString = pos - textObject->GetRange().GetStart();
5236
5237 wxString newText = textObject->GetText().Mid(0, posInString) +
5238 text + textObject->GetText().Mid(posInString);
5239 textObject->SetText(newText);
5240
28f92d74 5241 int textLength = text.length();
5d7836c4
JS
5242
5243 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5244 textObject->GetRange().GetEnd() + textLength));
5245
5246 // Increment the end range of subsequent fragments in this paragraph.
5247 // We'll set the paragraph range itself at a higher level.
5248
5249 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5250 while (node)
5251 {
5252 wxRichTextObject* child = node->GetData();
5253 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5254 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5255
5d7836c4
JS
5256 node = node->GetNext();
5257 }
5258
5259 return true;
5260 }
5261 else
5262 {
5263 // TODO: if not a text object, insert at closest position, e.g. in front of it
5264 }
5265 }
5266 else
5267 {
5268 // Add at end.
5269 // Don't pass parent initially to suppress auto-setting of parent range.
5270 // We'll do that at a higher level.
5271 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5272
5273 AppendChild(textObject);
5274 return true;
5275 }
5276
5277 return false;
5278}
5279
5280void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5281{
bec80f4f 5282 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5283}
5284
5285/// Clear the cached lines
5286void wxRichTextParagraph::ClearLines()
5287{
5288 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5289}
5290
5291/// Get/set the object size for the given range. Returns false if the range
5292/// is invalid for this object.
8db2e3ef 5293bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
5294{
5295 if (!range.IsWithin(GetRange()))
5296 return false;
5297
5298 if (flags & wxRICHTEXT_UNFORMATTED)
5299 {
5300 // Just use unformatted data, assume no line breaks
5d7836c4
JS
5301 wxSize sz;
5302
31778480
JS
5303 wxArrayInt childExtents;
5304 wxArrayInt* p;
5305 if (partialExtents)
5306 p = & childExtents;
5307 else
5308 p = NULL;
5309
a70eb13e
JS
5310 int maxDescent = 0;
5311 int maxAscent = 0;
5312 int maxLineHeight = 0;
5313
5d7836c4
JS
5314 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5315 while (node)
5316 {
5317 wxRichTextObject* child = node->GetData();
5318 if (!child->GetRange().IsOutside(range))
5319 {
cdaed652 5320 // Floating objects have a zero size within the paragraph.
e12b91a3 5321 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
cdaed652
VZ
5322 {
5323 if (partialExtents)
5324 {
5325 int lastSize;
5326 if (partialExtents->GetCount() > 0)
5327 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5328 else
5329 lastSize = 0;
5330
5331 partialExtents->Add(0 /* zero size */ + lastSize);
5332 }
5333 }
5334 else
5335 {
603f702b 5336 wxSize childSize;
4f3d5bc0 5337
603f702b
JS
5338 wxRichTextRange rangeToUse = range;
5339 rangeToUse.LimitTo(child->GetRange());
603f702b 5340 int childDescent = 0;
31778480 5341
7c9fdebe 5342 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
603f702b
JS
5343 // but it's only going to be used after caching has taken place.
5344 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5345 {
603f702b
JS
5346 childDescent = child->GetDescent();
5347 childSize = child->GetCachedSize();
2f45f554 5348
a70eb13e
JS
5349 if (childDescent == 0)
5350 {
5351 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5352 }
5353 else
5354 {
5355 maxDescent = wxMax(maxDescent, childDescent);
5356 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5357 }
5358
5359 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5360
5361 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5362 sz.x += childSize.x;
a70eb13e 5363 descent = maxDescent;
603f702b
JS
5364 }
5365 else if (child->IsTopLevel())
31778480 5366 {
603f702b
JS
5367 childDescent = child->GetDescent();
5368 childSize = child->GetCachedSize();
31778480 5369
a70eb13e
JS
5370 if (childDescent == 0)
5371 {
5372 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5373 }
5374 else
5375 {
5376 maxDescent = wxMax(maxDescent, childDescent);
5377 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5378 }
5379
5380 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5381
5382 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5383 sz.x += childSize.x;
a70eb13e
JS
5384 descent = maxDescent;
5385
5386 // FIXME: this won't change the original values.
5387 // Should we be calling GetRangeSize above instead of using cached values?
5388#if 0
603f702b 5389 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5390 {
603f702b
JS
5391 child->SetCachedSize(childSize);
5392 child->SetDescent(childDescent);
31778480 5393 }
a70eb13e 5394#endif
31778480 5395
603f702b
JS
5396 if (partialExtents)
5397 {
5398 int lastSize;
5399 if (partialExtents->GetCount() > 0)
5400 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5401 else
5402 lastSize = 0;
5403
5404 partialExtents->Add(childSize.x + lastSize);
5405 }
5406 }
8db2e3ef 5407 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b 5408 {
a70eb13e
JS
5409 if (childDescent == 0)
5410 {
5411 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5412 }
5413 else
5414 {
5415 maxDescent = wxMax(maxDescent, childDescent);
5416 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5417 }
5418
5419 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5420
5421 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5422 sz.x += childSize.x;
a70eb13e 5423 descent = maxDescent;
603f702b
JS
5424
5425 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5426 {
5427 child->SetCachedSize(childSize);
5428 child->SetDescent(childDescent);
5429 }
5430
5431 if (partialExtents)
5432 {
5433 int lastSize;
5434 if (partialExtents->GetCount() > 0)
5435 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5436 else
5437 lastSize = 0;
5438
5439 size_t i;
5440 for (i = 0; i < childExtents.GetCount(); i++)
5441 {
5442 partialExtents->Add(childExtents[i] + lastSize);
5443 }
5444 }
5445 }
5446 }
5447
5448 if (p)
5449 p->Clear();
5d7836c4
JS
5450 }
5451
5452 node = node->GetNext();
5453 }
5454 size = sz;
5455 }
5456 else
5457 {
5458 // Use formatted data, with line breaks
5459 wxSize sz;
5460
5461 // We're going to loop through each line, and then for each line,
5462 // call GetRangeSize for the fragment that comprises that line.
5463 // Only we have to do that multiple times within the line, because
5464 // the line may be broken into pieces. For now ignore line break commands
5465 // (so we can assume that getting the unformatted size for a fragment
5466 // within a line is the actual size)
5467
5468 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5469 while (node)
5470 {
5471 wxRichTextLine* line = node->GetData();
1e967276
JS
5472 wxRichTextRange lineRange = line->GetAbsoluteRange();
5473 if (!lineRange.IsOutside(range))
5d7836c4 5474 {
a70eb13e
JS
5475 int maxDescent = 0;
5476 int maxAscent = 0;
5477 int maxLineHeight = 0;
5478 int maxLineWidth = 0;
7fe8059f 5479
5d7836c4
JS
5480 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5481 while (node2)
5482 {
5483 wxRichTextObject* child = node2->GetData();
7fe8059f 5484
e12b91a3 5485 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5d7836c4 5486 {
1e967276 5487 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5488 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5489 if (child->IsTopLevel())
5490 rangeToUse = child->GetOwnRange();
7fe8059f 5491
5d7836c4
JS
5492 wxSize childSize;
5493 int childDescent = 0;
8db2e3ef 5494 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
5d7836c4 5495 {
a70eb13e
JS
5496 if (childDescent == 0)
5497 {
5498 // Assume that if descent is zero, this child can occupy the full line height
5499 // and does not need space for the line's maximum descent. So we influence
5500 // the overall max line height only.
5501 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5502 }
5503 else
5504 {
5505 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5506 maxDescent = wxMax(maxAscent, childDescent);
5507 }
5508 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5509 maxLineWidth += childSize.x;
5d7836c4 5510 }
5d7836c4 5511 }
7fe8059f 5512
5d7836c4
JS
5513 node2 = node2->GetNext();
5514 }
5515
a70eb13e
JS
5516 descent = wxMax(descent, maxDescent);
5517
5d7836c4 5518 // Increase size by a line (TODO: paragraph spacing)
a70eb13e
JS
5519 sz.y += maxLineHeight;
5520 sz.x = wxMax(sz.x, maxLineWidth);
5d7836c4
JS
5521 }
5522 node = node->GetNext();
5523 }
5524 size = sz;
5525 }
5526 return true;
5527}
5528
5529/// Finds the absolute position and row height for the given character position
8db2e3ef 5530bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5531{
5532 if (index == -1)
5533 {
5534 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5535 if (line)
5536 *height = line->GetSize().y;
5537 else
5538 *height = dc.GetCharHeight();
5539
5540 // -1 means 'the start of the buffer'.
5541 pt = GetPosition();
5542 if (line)
5543 pt = pt + line->GetPosition();
5544
5d7836c4
JS
5545 return true;
5546 }
5547
5548 // The final position in a paragraph is taken to mean the position
5549 // at the start of the next paragraph.
5550 if (index == GetRange().GetEnd())
5551 {
5552 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5553 wxASSERT( parent != NULL );
5554
5555 // Find the height at the next paragraph, if any
5556 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5557 if (line)
5558 {
5559 *height = line->GetSize().y;
5560 pt = line->GetAbsolutePosition();
5561 }
5562 else
5563 {
5564 *height = dc.GetCharHeight();
5565 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5566 pt = wxPoint(indent, GetCachedSize().y);
5567 }
5568
5569 return true;
5570 }
5571
5572 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5573 return false;
5574
5575 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5576 while (node)
5577 {
5578 wxRichTextLine* line = node->GetData();
1e967276
JS
5579 wxRichTextRange lineRange = line->GetAbsoluteRange();
5580 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5581 {
5582 // If this is the last point in the line, and we're forcing the
5583 // returned value to be the start of the next line, do the required
5584 // thing.
1e967276 5585 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5586 {
5587 if (node->GetNext())
5588 {
5589 wxRichTextLine* nextLine = node->GetNext()->GetData();
5590 *height = nextLine->GetSize().y;
5591 pt = nextLine->GetAbsolutePosition();
5592 return true;
5593 }
5594 }
5595
5596 pt.y = line->GetPosition().y + GetPosition().y;
5597
1e967276 5598 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5599 wxSize rangeSize;
5600 int descent = 0;
5601
5602 // We find the size of the line up to this point,
5603 // then we can add this size to the line start position and
5604 // paragraph start position to find the actual position.
5605
8db2e3ef 5606 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5607 {
5608 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5609 *height = line->GetSize().y;
5610
5611 return true;
5612 }
5613
5614 }
5615
5616 node = node->GetNext();
5617 }
5618
5619 return false;
5620}
5621
5622/// Hit-testing: returns a flag indicating hit test details, plus
5623/// information about position
8db2e3ef 5624int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5625{
603f702b
JS
5626 if (!IsShown())
5627 return wxRICHTEXT_HITTEST_NONE;
5628
5629 // If we're in the top-level container, then we can return
5630 // a suitable hit test code even if the point is outside the container area,
5631 // so that we can position the caret sensibly even if we don't
5632 // click on valid content. If we're not at the top-level, and the point
5633 // is not within this paragraph object, then we don't want to stop more
5634 // precise hit-testing from working prematurely, so return immediately.
5635 // NEW STRATEGY: use the parent boundary to test whether we're in the
5636 // right region, not the paragraph, since the paragraph may be positioned
5637 // some way in from where the user clicks.
5638 {
5639 long tmpPos;
5640 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5641 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5642 return wxRICHTEXT_HITTEST_NONE;
5643 }
5644
5645 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5646 while (objNode)
5647 {
5648 wxRichTextObject* child = objNode->GetData();
7c9fdebe
JS
5649 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5650 // and also, if this seems composite but actually is marked as atomic,
5651 // don't recurse.
5652 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5653 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
603f702b
JS
5654 {
5655 {
8db2e3ef 5656 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5657 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5658 return hitTest;
5659 }
5660 }
5661
5662 objNode = objNode->GetNext();
5663 }
5664
5d7836c4
JS
5665 wxPoint paraPos = GetPosition();
5666
5667 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5668 while (node)
5669 {
5670 wxRichTextLine* line = node->GetData();
5671 wxPoint linePos = paraPos + line->GetPosition();
5672 wxSize lineSize = line->GetSize();
1e967276 5673 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5674
62381daa 5675 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5676 {
5677 if (pt.x < linePos.x)
5678 {
1e967276 5679 textPosition = lineRange.GetStart();
603f702b
JS
5680 *obj = FindObjectAtPosition(textPosition);
5681 *contextObj = GetContainer();
f262b25c 5682 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5683 }
5684 else if (pt.x >= (linePos.x + lineSize.x))
5685 {
1e967276 5686 textPosition = lineRange.GetEnd();
603f702b
JS
5687 *obj = FindObjectAtPosition(textPosition);
5688 *contextObj = GetContainer();
f262b25c 5689 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5690 }
5691 else
5692 {
2f45f554
JS
5693#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5694 wxArrayInt partialExtents;
5695
5696 wxSize paraSize;
5697 int paraDescent;
5698
5699 // This calculates the partial text extents
8db2e3ef 5700 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
2f45f554
JS
5701
5702 int lastX = linePos.x;
5703 size_t i;
5704 for (i = 0; i < partialExtents.GetCount(); i++)
5705 {
5706 int nextX = partialExtents[i] + linePos.x;
5707
5708 if (pt.x >= lastX && pt.x <= nextX)
5709 {
5710 textPosition = i + lineRange.GetStart(); // minus 1?
5711
603f702b
JS
5712 *obj = FindObjectAtPosition(textPosition);
5713 *contextObj = GetContainer();
5714
2f45f554
JS
5715 // So now we know it's between i-1 and i.
5716 // Let's see if we can be more precise about
5717 // which side of the position it's on.
5718
cdaed652 5719 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5720 if (pt.x >= midPoint)
5721 return wxRICHTEXT_HITTEST_AFTER;
5722 else
5723 return wxRICHTEXT_HITTEST_BEFORE;
5724 }
5725
5726 lastX = nextX;
5727 }
5728#else
5d7836c4
JS
5729 long i;
5730 int lastX = linePos.x;
1e967276 5731 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5732 {
5733 wxSize childSize;
5734 int descent = 0;
7fe8059f 5735
1e967276 5736 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5737
8db2e3ef 5738 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5739
5740 int nextX = childSize.x + linePos.x;
5741
5742 if (pt.x >= lastX && pt.x <= nextX)
5743 {
5744 textPosition = i;
5745
603f702b
JS
5746 *obj = FindObjectAtPosition(textPosition);
5747 *contextObj = GetContainer();
5748
5d7836c4
JS
5749 // So now we know it's between i-1 and i.
5750 // Let's see if we can be more precise about
5751 // which side of the position it's on.
5752
cdaed652 5753 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5754 if (pt.x >= midPoint)
5755 return wxRICHTEXT_HITTEST_AFTER;
5756 else
5757 return wxRICHTEXT_HITTEST_BEFORE;
5758 }
5759 else
5760 {
5761 lastX = nextX;
5762 }
5763 }
2f45f554 5764#endif
5d7836c4
JS
5765 }
5766 }
7fe8059f 5767
5d7836c4
JS
5768 node = node->GetNext();
5769 }
5770
5771 return wxRICHTEXT_HITTEST_NONE;
5772}
5773
5774/// Split an object at this position if necessary, and return
5775/// the previous object, or NULL if inserting at beginning.
5776wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5777{
5778 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5779 while (node)
5780 {
5781 wxRichTextObject* child = node->GetData();
5782
5783 if (pos == child->GetRange().GetStart())
5784 {
5785 if (previousObject)
4d551ad5
JS
5786 {
5787 if (node->GetPrevious())
5788 *previousObject = node->GetPrevious()->GetData();
5789 else
5790 *previousObject = NULL;
5791 }
5d7836c4
JS
5792
5793 return child;
5794 }
5795
5796 if (child->GetRange().Contains(pos))
5797 {
5798 // This should create a new object, transferring part of
5799 // the content to the old object and the rest to the new object.
5800 wxRichTextObject* newObject = child->DoSplit(pos);
5801
5802 // If we couldn't split this object, just insert in front of it.
5803 if (!newObject)
5804 {
5805 // Maybe this is an empty string, try the next one
5806 // return child;
5807 }
5808 else
5809 {
5810 // Insert the new object after 'child'
5811 if (node->GetNext())
5812 m_children.Insert(node->GetNext(), newObject);
5813 else
5814 m_children.Append(newObject);
5815 newObject->SetParent(this);
5816
5817 if (previousObject)
5818 *previousObject = child;
5819
5820 return newObject;
5821 }
5822 }
5823
5824 node = node->GetNext();
5825 }
5826 if (previousObject)
5827 *previousObject = NULL;
5828 return NULL;
5829}
5830
5831/// Move content to a list from obj on
5832void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5833{
5834 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5835 while (node)
5836 {
5837 wxRichTextObject* child = node->GetData();
5838 list.Append(child);
5839
5840 wxRichTextObjectList::compatibility_iterator oldNode = node;
5841
5842 node = node->GetNext();
5843
5844 m_children.DeleteNode(oldNode);
5845 }
5846}
5847
5848/// Add content back from list
5849void wxRichTextParagraph::MoveFromList(wxList& list)
5850{
09f14108 5851 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5852 {
5853 AppendChild((wxRichTextObject*) node->GetData());
5854 }
5855}
5856
5857/// Calculate range
5858void wxRichTextParagraph::CalculateRange(long start, long& end)
5859{
5860 wxRichTextCompositeObject::CalculateRange(start, end);
5861
5862 // Add one for end of paragraph
5863 end ++;
5864
5865 m_range.SetRange(start, end);
5866}
5867
5868/// Find the object at the given position
5869wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5870{
5871 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5872 while (node)
5873 {
5874 wxRichTextObject* obj = node->GetData();
603f702b
JS
5875 if (obj->GetRange().Contains(position) ||
5876 obj->GetRange().GetStart() == position ||
5877 obj->GetRange().GetEnd() == position)
5d7836c4 5878 return obj;
7fe8059f 5879
5d7836c4
JS
5880 node = node->GetNext();
5881 }
5882 return NULL;
5883}
5884
5885/// Get the plain text searching from the start or end of the range.
5886/// The resulting string may be shorter than the range given.
5887bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5888{
5889 text = wxEmptyString;
5890
5891 if (fromStart)
5892 {
5893 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5894 while (node)
5895 {
5896 wxRichTextObject* obj = node->GetData();
5897 if (!obj->GetRange().IsOutside(range))
5898 {
5899 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5900 if (textObj)
5901 {
5902 text += textObj->GetTextForRange(range);
5903 }
5904 else
043c0d58
JS
5905 {
5906 text += wxT(" ");
5907 }
5d7836c4
JS
5908 }
5909
5910 node = node->GetNext();
5911 }
5912 }
5913 else
5914 {
5915 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5916 while (node)
5917 {
5918 wxRichTextObject* obj = node->GetData();
5919 if (!obj->GetRange().IsOutside(range))
5920 {
5921 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5922 if (textObj)
5923 {
5924 text = textObj->GetTextForRange(range) + text;
5925 }
5926 else
043c0d58
JS
5927 {
5928 text = wxT(" ") + text;
5929 }
5d7836c4
JS
5930 }
5931
5932 node = node->GetPrevious();
5933 }
5934 }
5935
5936 return true;
5937}
5938
5939/// Find a suitable wrap position.
8db2e3ef 5940bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5941{
72945e24
JS
5942 if (range.GetLength() <= 0)
5943 return false;
5944
5d7836c4
JS
5945 // Find the first position where the line exceeds the available space.
5946 wxSize sz;
5d7836c4 5947 long breakPosition = range.GetEnd();
ecb5fbf1 5948
31778480
JS
5949#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5950 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 5951 {
31778480 5952 int widthBefore;
5d7836c4 5953
31778480
JS
5954 if (range.GetStart() > GetRange().GetStart())
5955 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5956 else
5957 widthBefore = 0;
5958
5959 size_t i;
43a0d1e1 5960 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 5961 {
31778480 5962 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 5963
72945e24 5964 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 5965 {
31778480
JS
5966 breakPosition = i-1;
5967 break;
ecb5fbf1 5968 }
5d7836c4 5969 }
31778480
JS
5970 }
5971 else
5972#endif
5973 {
5974 // Binary chop for speed
5975 long minPos = range.GetStart();
5976 long maxPos = range.GetEnd();
5977 while (true)
ecb5fbf1 5978 {
31778480
JS
5979 if (minPos == maxPos)
5980 {
5981 int descent = 0;
8db2e3ef 5982 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 5983
31778480
JS
5984 if (sz.x > availableSpace)
5985 breakPosition = minPos - 1;
5986 break;
5987 }
5988 else if ((maxPos - minPos) == 1)
ecb5fbf1 5989 {
31778480 5990 int descent = 0;
8db2e3ef 5991 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5992
5993 if (sz.x > availableSpace)
5994 breakPosition = minPos - 1;
5995 else
5996 {
8db2e3ef 5997 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5998 if (sz.x > availableSpace)
5999 breakPosition = maxPos-1;
6000 }
6001 break;
ecb5fbf1
JS
6002 }
6003 else
6004 {
31778480
JS
6005 long nextPos = minPos + ((maxPos - minPos) / 2);
6006
6007 int descent = 0;
8db2e3ef 6008 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6009
6010 if (sz.x > availableSpace)
6011 {
6012 maxPos = nextPos;
6013 }
6014 else
6015 {
6016 minPos = nextPos;
6017 }
ecb5fbf1
JS
6018 }
6019 }
5d7836c4
JS
6020 }
6021
6022 // Now we know the last position on the line.
6023 // Let's try to find a word break.
6024
6025 wxString plainText;
6026 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6027 {
ff76711f
JS
6028 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6029 if (newLinePos != wxNOT_FOUND)
5d7836c4 6030 {
ff76711f
JS
6031 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6032 }
6033 else
6034 {
6035 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
6036 int tabPos = plainText.Find(wxT('\t'), true);
6037 int pos = wxMax(spacePos, tabPos);
6038 if (pos != wxNOT_FOUND)
ff76711f 6039 {
31002e44 6040 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
6041 breakPosition = breakPosition - positionsFromEndOfString;
6042 }
5d7836c4
JS
6043 }
6044 }
6045
6046 wrapPosition = breakPosition;
6047
6048 return true;
6049}
6050
6051/// Get the bullet text for this paragraph.
6052wxString wxRichTextParagraph::GetBulletText()
6053{
6054 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6055 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6056 return wxEmptyString;
6057
6058 int number = GetAttributes().GetBulletNumber();
6059
6060 wxString text;
d2d0adc7 6061 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
6062 {
6063 text.Printf(wxT("%d"), number);
6064 }
6065 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6066 {
6067 // TODO: Unicode, and also check if number > 26
6068 text.Printf(wxT("%c"), (wxChar) (number+64));
6069 }
6070 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6071 {
6072 // TODO: Unicode, and also check if number > 26
6073 text.Printf(wxT("%c"), (wxChar) (number+96));
6074 }
6075 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6076 {
59509217 6077 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
6078 }
6079 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6080 {
59509217
JS
6081 text = wxRichTextDecimalToRoman(number);
6082 text.MakeLower();
5d7836c4
JS
6083 }
6084 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6085 {
d2d0adc7
JS
6086 text = GetAttributes().GetBulletText();
6087 }
3e541562 6088
d2d0adc7
JS
6089 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6090 {
6091 // The outline style relies on the text being computed statically,
6092 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6093 // should be stored in the attributes; if not, just use the number for this
6094 // level, as previously computed.
6095 if (!GetAttributes().GetBulletText().IsEmpty())
6096 text = GetAttributes().GetBulletText();
5d7836c4
JS
6097 }
6098
6099 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6100 {
6101 text = wxT("(") + text + wxT(")");
6102 }
d2d0adc7
JS
6103 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6104 {
6105 text = text + wxT(")");
6106 }
6107
5d7836c4
JS
6108 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6109 {
6110 text += wxT(".");
6111 }
6112
6113 return text;
6114}
6115
1e967276
JS
6116/// Allocate or reuse a line object
6117wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6118{
6119 if (pos < (int) m_cachedLines.GetCount())
6120 {
6121 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6122 line->Init(this);
6123 return line;
6124 }
6125 else
6126 {
6127 wxRichTextLine* line = new wxRichTextLine(this);
6128 m_cachedLines.Append(line);
6129 return line;
6130 }
6131}
6132
6133/// Clear remaining unused line objects, if any
6134bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6135{
6136 int cachedLineCount = m_cachedLines.GetCount();
6137 if ((int) cachedLineCount > lineCount)
6138 {
6139 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6140 {
6141 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6142 wxRichTextLine* line = node->GetData();
6143 m_cachedLines.Erase(node);
6144 delete line;
6145 }
6146 }
6147 return true;
6148}
6149
fe5aa22c
JS
6150/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6151/// retrieve the actual style.
603f702b 6152wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 6153{
24777478 6154 wxRichTextAttr attr;
603f702b 6155 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6156 if (buf)
6157 {
6158 attr = buf->GetBasicStyle();
603f702b
JS
6159 if (!includingBoxAttr)
6160 {
6161 attr.GetTextBoxAttr().Reset();
6162 // The background colour will be painted by the container, and we don't
6163 // want to unnecessarily overwrite the background when we're drawing text
6164 // because this may erase the guideline (which appears just under the text
6165 // if there's no padding).
6166 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6167 }
fe5aa22c
JS
6168 wxRichTextApplyStyle(attr, GetAttributes());
6169 }
6170 else
6171 attr = GetAttributes();
6172
6173 wxRichTextApplyStyle(attr, contentStyle);
6174 return attr;
6175}
6176
6177/// Get combined attributes of the base style and paragraph style.
603f702b 6178wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 6179{
24777478 6180 wxRichTextAttr attr;
603f702b 6181 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6182 if (buf)
6183 {
6184 attr = buf->GetBasicStyle();
603f702b
JS
6185 if (!includingBoxAttr)
6186 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
6187 wxRichTextApplyStyle(attr, GetAttributes());
6188 }
6189 else
6190 attr = GetAttributes();
6191
6192 return attr;
6193}
5d7836c4 6194
603f702b 6195// Create default tabstop array
cfa3b256
JS
6196void wxRichTextParagraph::InitDefaultTabs()
6197{
6198 // create a default tab list at 10 mm each.
6199 for (int i = 0; i < 20; ++i)
6200 {
6201 sm_defaultTabs.Add(i*100);
6202 }
6203}
6204
603f702b 6205// Clear default tabstop array
cfa3b256
JS
6206void wxRichTextParagraph::ClearDefaultTabs()
6207{
6208 sm_defaultTabs.Clear();
6209}
6210
c4168888 6211void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
6212{
6213 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6214 while (node)
6215 {
bec80f4f 6216 wxRichTextObject* anchored = node->GetData();
07d4142f 6217 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652 6218 {
c4168888
JS
6219 int x = 0;
6220 wxRichTextAttr parentAttr(GetAttributes());
6221 context.ApplyVirtualAttributes(parentAttr, this);
6222#if 1
6223 // 27-09-2012
6224 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6225
6226 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6227 parentAttr, anchored->GetAttributes(),
6228 parentRect, availableSpace,
6229 style);
6230 wxSize size = anchored->GetCachedSize();
6231#else
cdaed652 6232 wxSize size;
c4168888 6233 int descent = 0;
8db2e3ef 6234 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
c4168888 6235#endif
bec80f4f 6236
24777478 6237 int offsetY = 0;
603f702b 6238 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
6239 {
6240 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6241 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6242 {
6243 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6244 }
6245 }
bec80f4f 6246
24777478 6247 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6248
cdaed652 6249 /* Update the offset */
24777478
JS
6250 int newOffsetY = pos - rect.y;
6251 if (newOffsetY != offsetY)
6252 {
6253 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6254 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6255 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6256 }
cdaed652 6257
24777478 6258 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6259 x = rect.x;
24777478 6260 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6261 x = rect.x + rect.width - size.x;
24777478 6262
c4168888
JS
6263 //anchored->SetPosition(wxPoint(x, pos));
6264 anchored->Move(wxPoint(x, pos)); // should move children
cdaed652
VZ
6265 anchored->SetCachedSize(size);
6266 floatCollector->CollectFloat(this, anchored);
6267 }
6268
6269 node = node->GetNext();
6270 }
6271}
6272
603f702b 6273// Get the first position from pos that has a line break character.
ff76711f
JS
6274long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6275{
6276 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6277 while (node)
6278 {
6279 wxRichTextObject* obj = node->GetData();
6280 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6281 {
6282 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6283 if (textObj)
6284 {
6285 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6286 if (breakPos > -1)
6287 return breakPos;
6288 }
6289 }
6290 node = node->GetNext();
6291 }
6292 return -1;
6293}
cfa3b256 6294
5d7836c4
JS
6295/*!
6296 * wxRichTextLine
6297 * This object represents a line in a paragraph, and stores
6298 * offsets from the start of the paragraph representing the
6299 * start and end positions of the line.
6300 */
6301
6302wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6303{
1e967276 6304 Init(parent);
5d7836c4
JS
6305}
6306
6307/// Initialisation
1e967276 6308void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6309{
1e967276
JS
6310 m_parent = parent;
6311 m_range.SetRange(-1, -1);
6312 m_pos = wxPoint(0, 0);
6313 m_size = wxSize(0, 0);
5d7836c4 6314 m_descent = 0;
2f45f554
JS
6315#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6316 m_objectSizes.Clear();
6317#endif
5d7836c4
JS
6318}
6319
6320/// Copy
6321void wxRichTextLine::Copy(const wxRichTextLine& obj)
6322{
6323 m_range = obj.m_range;
2f45f554
JS
6324#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6325 m_objectSizes = obj.m_objectSizes;
6326#endif
5d7836c4
JS
6327}
6328
6329/// Get the absolute object position
6330wxPoint wxRichTextLine::GetAbsolutePosition() const
6331{
6332 return m_parent->GetPosition() + m_pos;
6333}
6334
1e967276
JS
6335/// Get the absolute range
6336wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6337{
6338 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6339 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6340 return range;
6341}
6342
5d7836c4
JS
6343/*!
6344 * wxRichTextPlainText
6345 * This object represents a single piece of text.
6346 */
6347
6348IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6349
24777478 6350wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6351 wxRichTextObject(parent)
6352{
5d7836c4
JS
6353 if (style)
6354 SetAttributes(*style);
6355
6356 m_text = text;
6357}
6358
cfa3b256
JS
6359#define USE_KERNING_FIX 1
6360
4794d69c
JS
6361// If insufficient tabs are defined, this is the tab width used
6362#define WIDTH_FOR_DEFAULT_TABS 50
6363
5d7836c4 6364/// Draw the item
8db2e3ef 6365bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6366{
fe5aa22c
JS
6367 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6368 wxASSERT (para != NULL);
6369
603f702b 6370 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6371 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6372
6373 // Let's make the assumption for now that for content in a paragraph, including
6374 // text, we never have a discontinuous selection. So we only deal with a
6375 // single range.
6376 wxRichTextRange selectionRange;
6377 if (selection.IsValid())
6378 {
6379 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6380 if (selectionRanges.GetCount() > 0)
6381 selectionRange = selectionRanges[0];
6382 else
6383 selectionRange = wxRICHTEXT_NO_SELECTION;
6384 }
6385 else
6386 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6387
5d7836c4
JS
6388 int offset = GetRange().GetStart();
6389
ff76711f 6390 wxString str = m_text;
f7667b84
JS
6391 if (context.HasVirtualText(this))
6392 {
6393 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6394 str = m_text;
6395 }
6396
6397 // Replace line break characters with spaces
ff76711f
JS
6398 wxString toRemove = wxRichTextLineBreakChar;
6399 str.Replace(toRemove, wxT(" "));
d07f2e19 6400 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
c025e094 6401 str.MakeUpper();
3e541562 6402
5d7836c4 6403 long len = range.GetLength();
ff76711f 6404 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6405
5d7836c4
JS
6406 // Test for the optimized situations where all is selected, or none
6407 // is selected.
6408
30bf7630
JS
6409 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6410 wxCheckSetFont(dc, textFont);
6411 int charHeight = dc.GetCharHeight();
6412
6413 int x, y;
a1b806b9 6414 if ( textFont.IsOk() )
30bf7630 6415 {
d07f2e19
JS
6416 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6417 {
6418 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6419 wxCheckSetFont(dc, textFont);
6420 charHeight = dc.GetCharHeight();
6421 }
6422
30bf7630
JS
6423 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6424 {
32423dd8
JS
6425 if (textFont.IsUsingSizeInPixels())
6426 {
6427 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
4ba36292 6428 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
32423dd8
JS
6429 x = rect.x;
6430 y = rect.y;
6431 }
6432 else
6433 {
6434 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6435 textFont.SetPointSize(static_cast<int>(size));
32423dd8
JS
6436 x = rect.x;
6437 y = rect.y;
6438 }
30bf7630
JS
6439 wxCheckSetFont(dc, textFont);
6440 }
6441 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6442 {
32423dd8
JS
6443 if (textFont.IsUsingSizeInPixels())
6444 {
6445 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6446 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6447 x = rect.x;
4ba36292 6448 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6449 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6450 }
6451 else
6452 {
6453 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6454 textFont.SetPointSize(static_cast<int>(size));
32423dd8 6455 x = rect.x;
4ba36292 6456 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6457 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6458 }
30bf7630
JS
6459 wxCheckSetFont(dc, textFont);
6460 }
6461 else
6462 {
6463 x = rect.x;
6464 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6465 }
6466 }
6467 else
6468 {
6469 x = rect.x;
6470 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6471 }
5d7836c4 6472
603f702b
JS
6473 // TODO: new selection code
6474
5d7836c4
JS
6475 // (a) All selected.
6476 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6477 {
fe5aa22c 6478 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6479 }
6480 // (b) None selected.
6481 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6482 {
6483 // Draw all unselected
fe5aa22c 6484 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6485 }
6486 else
6487 {
6488 // (c) Part selected, part not
6489 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6490
04ee05f9 6491 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6492
5d7836c4
JS
6493 // 1. Initial unselected chunk, if any, up until start of selection.
6494 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6495 {
6496 int r1 = range.GetStart();
6497 int s1 = selectionRange.GetStart()-1;
6498 int fragmentLen = s1 - r1 + 1;
6499 if (fragmentLen < 0)
af588446 6500 {
5d7836c4 6501 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6502 }
ff76711f 6503 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6504
fe5aa22c 6505 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6506
6507#if USE_KERNING_FIX
6508 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6509 {
6510 // Compensate for kerning difference
ff76711f
JS
6511 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6512 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6513
cfa3b256
JS
6514 wxCoord w1, h1, w2, h2, w3, h3;
6515 dc.GetTextExtent(stringFragment, & w1, & h1);
6516 dc.GetTextExtent(stringFragment2, & w2, & h2);
6517 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6518
cfa3b256
JS
6519 int kerningDiff = (w1 + w3) - w2;
6520 x = x - kerningDiff;
6521 }
6522#endif
5d7836c4
JS
6523 }
6524
6525 // 2. Selected chunk, if any.
6526 if (selectionRange.GetEnd() >= range.GetStart())
6527 {
6528 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6529 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6530
6531 int fragmentLen = s2 - s1 + 1;
6532 if (fragmentLen < 0)
af588446 6533 {
5d7836c4 6534 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6535 }
ff76711f 6536 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6537
fe5aa22c 6538 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6539
6540#if USE_KERNING_FIX
6541 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6542 {
6543 // Compensate for kerning difference
ff76711f
JS
6544 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6545 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6546
cfa3b256
JS
6547 wxCoord w1, h1, w2, h2, w3, h3;
6548 dc.GetTextExtent(stringFragment, & w1, & h1);
6549 dc.GetTextExtent(stringFragment2, & w2, & h2);
6550 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6551
cfa3b256
JS
6552 int kerningDiff = (w1 + w3) - w2;
6553 x = x - kerningDiff;
6554 }
6555#endif
5d7836c4
JS
6556 }
6557
6558 // 3. Remaining unselected chunk, if any
6559 if (selectionRange.GetEnd() < range.GetEnd())
6560 {
6561 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6562 int r2 = range.GetEnd();
6563
6564 int fragmentLen = r2 - s2 + 1;
6565 if (fragmentLen < 0)
af588446 6566 {
5d7836c4 6567 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6568 }
ff76711f 6569 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6570
fe5aa22c 6571 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6572 }
5d7836c4
JS
6573 }
6574
6575 return true;
6576}
61399247 6577
24777478 6578bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6579{
cfa3b256
JS
6580 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6581
6582 wxArrayInt tabArray;
6583 int tabCount;
6584 if (hasTabs)
ab14c7aa 6585 {
cfa3b256
JS
6586 if (attr.GetTabs().IsEmpty())
6587 tabArray = wxRichTextParagraph::GetDefaultTabs();
6588 else
6589 tabArray = attr.GetTabs();
6590 tabCount = tabArray.GetCount();
6591
6592 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6593 {
cfa3b256
JS
6594 int pos = tabArray[i];
6595 pos = ConvertTenthsMMToPixels(dc, pos);
6596 tabArray[i] = pos;
7f0d9d71
JS
6597 }
6598 }
cfa3b256
JS
6599 else
6600 tabCount = 0;
ab14c7aa 6601
cfa3b256
JS
6602 int nextTabPos = -1;
6603 int tabPos = -1;
7f0d9d71 6604 wxCoord w, h;
ab14c7aa 6605
cfa3b256 6606 if (selected)
ab14c7aa 6607 {
0ec6da02
JS
6608 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6609 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6610
ecb5fbf1
JS
6611 wxCheckSetBrush(dc, wxBrush(highlightColour));
6612 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6613 dc.SetTextForeground(highlightTextColour);
04ee05f9 6614 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6615 }
ab14c7aa
JS
6616 else
6617 {
fe5aa22c 6618 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6619
f0e9eda2
JS
6620 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6621 {
04ee05f9 6622 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6623 dc.SetTextBackground(attr.GetBackgroundColour());
6624 }
6625 else
04ee05f9 6626 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6627 }
3e541562 6628
925a662a 6629 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6630 while (hasTabs)
ab14c7aa
JS
6631 {
6632 // the string has a tab
7f0d9d71
JS
6633 // break up the string at the Tab
6634 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6635 str = str.AfterFirst(wxT('\t'));
6636 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6637 tabPos = x + w;
7f0d9d71 6638 bool not_found = true;
cfa3b256 6639 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6640 {
015d0446 6641 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6642
6643 // Find the next tab position.
6644 // Even if we're at the end of the tab array, we must still draw the chunk.
6645
6646 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6647 {
4794d69c
JS
6648 if (nextTabPos <= tabPos)
6649 {
6650 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6651 nextTabPos = tabPos + defaultTabWidth;
6652 }
6653
7f0d9d71 6654 not_found = false;
ab14c7aa
JS
6655 if (selected)
6656 {
cfa3b256 6657 w = nextTabPos - x;
7f0d9d71 6658 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6659 dc.DrawRectangle(selRect);
7f0d9d71
JS
6660 }
6661 dc.DrawText(stringChunk, x, y);
42688aea
JS
6662
6663 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6664 {
6665 wxPen oldPen = dc.GetPen();
ecb5fbf1 6666 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6667 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6668 wxCheckSetPen(dc, oldPen);
42688aea
JS
6669 }
6670
cfa3b256 6671 x = nextTabPos;
7f0d9d71
JS
6672 }
6673 }
cfa3b256 6674 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6675 }
61399247 6676
cfa3b256 6677 if (!str.IsEmpty())
ab14c7aa 6678 {
cfa3b256
JS
6679 dc.GetTextExtent(str, & w, & h);
6680 if (selected)
6681 {
6682 wxRect selRect(x, rect.y, w, rect.GetHeight());
6683 dc.DrawRectangle(selRect);
6684 }
6685 dc.DrawText(str, x, y);
42688aea
JS
6686
6687 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6688 {
6689 wxPen oldPen = dc.GetPen();
ecb5fbf1 6690 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6691 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6692 wxCheckSetPen(dc, oldPen);
42688aea
JS
6693 }
6694
cfa3b256 6695 x += w;
7f0d9d71 6696 }
5d7836c4 6697
7c9fdebe 6698 return true;
7f0d9d71 6699}
fe5aa22c 6700
5d7836c4 6701/// Lay the item out
8db2e3ef 6702bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6703{
ecb5fbf1
JS
6704 // Only lay out if we haven't already cached the size
6705 if (m_size.x == -1)
8db2e3ef 6706 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6707 m_maxSize = m_size;
6708 // Eventually we want to have a reasonable estimate of minimum size.
6709 m_minSize = wxSize(0, 0);
5d7836c4
JS
6710 return true;
6711}
6712
6713/// Copy
6714void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6715{
6716 wxRichTextObject::Copy(obj);
6717
6718 m_text = obj.m_text;
6719}
6720
6721/// Get/set the object size for the given range. Returns false if the range
6722/// is invalid for this object.
8db2e3ef 6723bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
6724{
6725 if (!range.IsWithin(GetRange()))
6726 return false;
6727
fe5aa22c
JS
6728 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6729 wxASSERT (para != NULL);
603f702b 6730
925a662a 6731 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6732
24777478 6733 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6734 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6735
5d7836c4
JS
6736 // Always assume unformatted text, since at this level we have no knowledge
6737 // of line breaks - and we don't need it, since we'll calculate size within
6738 // formatted text by doing it in chunks according to the line ranges
6739
30bf7630 6740 bool bScript(false);
44cc96a8 6741 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6742 if (font.IsOk())
30bf7630
JS
6743 {
6744 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6745 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6746 {
6747 wxFont textFont = font;
32423dd8
JS
6748 if (textFont.IsUsingSizeInPixels())
6749 {
6750 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6751 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6752 }
6753 else
6754 {
6755 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6756 textFont.SetPointSize(static_cast<int>(size));
6757 }
30bf7630
JS
6758 wxCheckSetFont(dc, textFont);
6759 bScript = true;
6760 }
d07f2e19
JS
6761 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6762 {
6763 wxFont textFont = font;
6764 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6765 wxCheckSetFont(dc, textFont);
6766 bScript = true;
6767 }
30bf7630
JS
6768 else
6769 {
6770 wxCheckSetFont(dc, font);
6771 }
6772 }
5d7836c4 6773
109bfc88 6774 bool haveDescent = false;
5d7836c4
JS
6775 int startPos = range.GetStart() - GetRange().GetStart();
6776 long len = range.GetLength();
3e541562 6777
ff76711f 6778 wxString str(m_text);
f7667b84
JS
6779 if (context.HasVirtualText(this))
6780 {
6781 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6782 str = m_text;
6783 }
6784
ff76711f
JS
6785 wxString toReplace = wxRichTextLineBreakChar;
6786 str.Replace(toReplace, wxT(" "));
6787
6788 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea 6789
d07f2e19 6790 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
42688aea
JS
6791 stringChunk.MakeUpper();
6792
5d7836c4 6793 wxCoord w, h;
7f0d9d71 6794 int width = 0;
cfa3b256 6795 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6796 {
6797 // the string has a tab
cfa3b256
JS
6798 wxArrayInt tabArray;
6799 if (textAttr.GetTabs().IsEmpty())
6800 tabArray = wxRichTextParagraph::GetDefaultTabs();
6801 else
6802 tabArray = textAttr.GetTabs();
ab14c7aa 6803
cfa3b256 6804 int tabCount = tabArray.GetCount();
41a85215 6805
cfa3b256 6806 for (int i = 0; i < tabCount; ++i)
61399247 6807 {
cfa3b256
JS
6808 int pos = tabArray[i];
6809 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6810 tabArray[i] = pos;
7f0d9d71 6811 }
41a85215 6812
cfa3b256 6813 int nextTabPos = -1;
61399247 6814
ab14c7aa
JS
6815 while (stringChunk.Find(wxT('\t')) >= 0)
6816 {
109bfc88
JS
6817 int absoluteWidth = 0;
6818
ab14c7aa 6819 // the string has a tab
7f0d9d71
JS
6820 // break up the string at the Tab
6821 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6822 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6823
31778480
JS
6824 if (partialExtents)
6825 {
109bfc88
JS
6826 int oldWidth;
6827 if (partialExtents->GetCount() > 0)
6828 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6829 else
6830 oldWidth = 0;
6831
31778480
JS
6832 // Add these partial extents
6833 wxArrayInt p;
6834 dc.GetPartialTextExtents(stringFragment, p);
6835 size_t j;
6836 for (j = 0; j < p.GetCount(); j++)
6837 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6838
6839 if (partialExtents->GetCount() > 0)
925a662a 6840 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6841 else
925a662a 6842 absoluteWidth = relativeX;
109bfc88
JS
6843 }
6844 else
6845 {
6846 dc.GetTextExtent(stringFragment, & w, & h);
6847 width += w;
603f702b 6848 absoluteWidth = width + relativeX;
109bfc88 6849 haveDescent = true;
31778480
JS
6850 }
6851
cfa3b256
JS
6852 bool notFound = true;
6853 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6854 {
cfa3b256 6855 nextTabPos = tabArray.Item(i);
4794d69c
JS
6856
6857 // Find the next tab position.
6858 // Even if we're at the end of the tab array, we must still process the chunk.
6859
6860 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6861 {
4794d69c
JS
6862 if (nextTabPos <= absoluteWidth)
6863 {
6864 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6865 nextTabPos = absoluteWidth + defaultTabWidth;
6866 }
6867
cfa3b256 6868 notFound = false;
925a662a 6869 width = nextTabPos - relativeX;
31778480
JS
6870
6871 if (partialExtents)
6872 partialExtents->Add(width);
7f0d9d71
JS
6873 }
6874 }
6875 }
6876 }
30bf7630 6877
31778480
JS
6878 if (!stringChunk.IsEmpty())
6879 {
31778480
JS
6880 if (partialExtents)
6881 {
109bfc88
JS
6882 int oldWidth;
6883 if (partialExtents->GetCount() > 0)
6884 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6885 else
6886 oldWidth = 0;
6887
31778480
JS
6888 // Add these partial extents
6889 wxArrayInt p;
6890 dc.GetPartialTextExtents(stringChunk, p);
6891 size_t j;
6892 for (j = 0; j < p.GetCount(); j++)
6893 partialExtents->Add(oldWidth + p[j]);
6894 }
109bfc88
JS
6895 else
6896 {
6897 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6898 width += w;
6899 haveDescent = true;
6900 }
6901 }
6902
6903 if (partialExtents)
6904 {
6905 int charHeight = dc.GetCharHeight();
6906 if ((*partialExtents).GetCount() > 0)
6907 w = (*partialExtents)[partialExtents->GetCount()-1];
6908 else
6909 w = 0;
6910 size = wxSize(w, charHeight);
6911 }
6912 else
6913 {
6914 size = wxSize(width, dc.GetCharHeight());
31778480 6915 }
30bf7630 6916
109bfc88
JS
6917 if (!haveDescent)
6918 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6919
30bf7630
JS
6920 if ( bScript )
6921 dc.SetFont(font);
6922
5d7836c4
JS
6923 return true;
6924}
6925
6926/// Do a split, returning an object containing the second part, and setting
6927/// the first part in 'this'.
6928wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6929{
ff76711f 6930 long index = pos - GetRange().GetStart();
3e541562 6931
28f92d74 6932 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6933 return NULL;
6934
6935 wxString firstPart = m_text.Mid(0, index);
6936 wxString secondPart = m_text.Mid(index);
6937
6938 m_text = firstPart;
6939
6940 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6941 newObject->SetAttributes(GetAttributes());
8db2e3ef 6942 newObject->SetProperties(GetProperties());
5d7836c4
JS
6943
6944 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6945 GetRange().SetEnd(pos-1);
3e541562 6946
5d7836c4
JS
6947 return newObject;
6948}
6949
6950/// Calculate range
6951void wxRichTextPlainText::CalculateRange(long start, long& end)
6952{
28f92d74 6953 end = start + m_text.length() - 1;
5d7836c4
JS
6954 m_range.SetRange(start, end);
6955}
6956
6957/// Delete range
6958bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6959{
6960 wxRichTextRange r = range;
6961
6962 r.LimitTo(GetRange());
6963
6964 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6965 {
6966 m_text.Empty();
6967 return true;
6968 }
6969
6970 long startIndex = r.GetStart() - GetRange().GetStart();
6971 long len = r.GetLength();
6972
6973 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6974 return true;
6975}
6976
6977/// Get text for the given range.
6978wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6979{
6980 wxRichTextRange r = range;
6981
6982 r.LimitTo(GetRange());
6983
6984 long startIndex = r.GetStart() - GetRange().GetStart();
6985 long len = r.GetLength();
6986
6987 return m_text.Mid(startIndex, len);
6988}
6989
6990/// Returns true if this object can merge itself with the given one.
f7667b84 6991bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
5d7836c4 6992{
f7667b84
JS
6993 // JACS 2013-01-27
6994 if (!context.GetVirtualAttributesEnabled())
6995 {
6996 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
6997 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
6998 }
6999 else
7000 {
7001 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7002 if (!otherObj || m_text.empty())
7003 return false;
7004
7005 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7006 return false;
7007
7008 // Check if differing virtual attributes makes it impossible to merge
7009 // these strings.
7010
7011 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7012 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7013 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7014 return true;
7015 else if (hasVirtualAttr1 != hasVirtualAttr2)
7016 return false;
7017 else
7018 {
7019 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7020 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7021 return virtualAttr1 == virtualAttr2;
7022 }
7023 }
5d7836c4
JS
7024}
7025
7026/// Returns true if this object merged itself with the given one.
7027/// The calling code will then delete the given object.
f7667b84 7028bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
5d7836c4
JS
7029{
7030 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7031 wxASSERT( textObject != NULL );
7032
7033 if (textObject)
7034 {
7035 m_text += textObject->GetText();
99404ab0 7036 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
7037 return true;
7038 }
7039 else
7040 return false;
7041}
7042
f7667b84
JS
7043bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7044{
7045 // If this object has any virtual attributes at all, whether for the whole object
7046 // or individual ones, we should try splitting it by calling Split.
7047 // Must be more than one character in order to be able to split.
7048 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7049}
7050
7051wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7052{
7053 int count = context.GetVirtualSubobjectAttributesCount(this);
7054 if (count > 0 && GetParent())
7055 {
7056 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7057 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7058 if (node)
7059 {
7060 const wxRichTextAttr emptyAttr;
7061 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7062
7063 wxArrayInt positions;
7064 wxRichTextAttrArray attributes;
7065 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7066 {
7067 wxASSERT(positions.GetCount() == attributes.GetCount());
7068
7069 // We will gather up runs of text with the same virtual attributes
7070
7071 int len = m_text.Length();
7072 int i = 0;
7073
7074 // runStart and runEnd represent the accumulated run with a consistent attribute
7075 // that hasn't yet been appended
7076 int runStart = -1;
7077 int runEnd = -1;
7078 wxRichTextAttr currentAttr;
7079 wxString text = m_text;
7080 wxRichTextPlainText* lastPlainText = this;
7081
7082 for (i = 0; i < (int) positions.GetCount(); i++)
7083 {
7084 int pos = positions[i];
7085 wxASSERT(pos >= 0 && pos < len);
7086 if (pos >= 0 && pos < len)
7087 {
7088 const wxRichTextAttr& attr = attributes[i];
7089
7090 if (pos == 0)
7091 {
7092 runStart = 0;
7093 currentAttr = attr;
7094 }
7095 // Check if there was a gap from the last known attribute and this.
7096 // In that case, we need to do something with the span of non-attributed text.
7097 else if ((pos-1) > runEnd)
7098 {
7099 if (runEnd == -1)
7100 {
7101 // We hadn't processed anything previously, so the previous run is from the text start
7102 // to just before this position. The current attribute remains empty.
7103 runStart = 0;
7104 runEnd = pos-1;
7105 }
7106 else
7107 {
7108 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7109 // then just extend the run.
7110 if (currentAttr.IsDefault())
7111 {
7112 runEnd = pos-1;
7113 }
7114 else
7115 {
7116 // We need to add an object, or reuse the existing one.
7117 if (runStart == 0)
7118 {
7119 lastPlainText = this;
7120 SetText(text.Mid(runStart, runEnd - runStart + 1));
7121 }
7122 else
7123 {
7124 wxRichTextPlainText* obj = new wxRichTextPlainText;
7125 lastPlainText = obj;
7126 obj->SetAttributes(GetAttributes());
7127 obj->SetProperties(GetProperties());
7128 obj->SetParent(parent);
7129
7130 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7131 if (next)
7132 parent->GetChildren().Insert(next, obj);
7133 else
7134 parent->GetChildren().Append(obj);
7135 }
7136
7137 runStart = runEnd+1;
7138 runEnd = pos-1;
7139
7140 currentAttr = emptyAttr;
7141 }
7142 }
7143 }
7144
7145 wxASSERT(runEnd == pos-1);
7146
7147 // Now we only have to deal with the previous run
7148 if (currentAttr == attr)
7149 {
7150 // If we still have the same attributes, then we
7151 // simply increase the run size.
7152 runEnd = pos;
7153 }
7154 else
7155 {
7156 if (runEnd >= 0)
7157 {
7158 // We need to add an object, or reuse the existing one.
7159 if (runStart == 0)
7160 {
7161 lastPlainText = this;
7162 SetText(text.Mid(runStart, runEnd - runStart + 1));
7163 }
7164 else
7165 {
7166 wxRichTextPlainText* obj = new wxRichTextPlainText;
7167 lastPlainText = obj;
7168 obj->SetAttributes(GetAttributes());
7169 obj->SetProperties(GetProperties());
7170 obj->SetParent(parent);
7171
7172 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7173 if (next)
7174 parent->GetChildren().Insert(next, obj);
7175 else
7176 parent->GetChildren().Append(obj);
7177 }
7178 }
7179
7180 runStart = pos;
7181 runEnd = pos;
7182
7183 currentAttr = attr;
7184 }
7185 }
7186 }
7187
7188 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7189 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7190 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7191 {
7192 // If the current attribute is empty, merge the run with the next fragment
7193 // which by definition (because it's not specified) has empty attributes.
7194 if (currentAttr.IsDefault())
7195 runEnd = (len-1);
7196
7197 if (runEnd < (len-1))
7198 {
7199 // We need to add an object, or reuse the existing one.
7200 if (runStart == 0)
7201 {
7202 lastPlainText = this;
7203 SetText(text.Mid(runStart, runEnd - runStart + 1));
7204 }
7205 else
7206 {
7207 wxRichTextPlainText* obj = new wxRichTextPlainText;
7208 lastPlainText = obj;
7209 obj->SetAttributes(GetAttributes());
7210 obj->SetProperties(GetProperties());
7211 obj->SetParent(parent);
7212
7213 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7214 if (next)
7215 parent->GetChildren().Insert(next, obj);
7216 else
7217 parent->GetChildren().Append(obj);
7218 }
7219
7220 runStart = runEnd+1;
7221 runEnd = (len-1);
7222 }
7223
7224 // Now the last, non-attributed fragment at the end, if any
7225 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7226 {
7227 wxASSERT(runStart != 0);
7228
7229 wxRichTextPlainText* obj = new wxRichTextPlainText;
7230 obj->SetAttributes(GetAttributes());
7231 obj->SetProperties(GetProperties());
7232 obj->SetParent(parent);
7233
7234 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7235 if (next)
7236 parent->GetChildren().Insert(next, obj);
7237 else
7238 parent->GetChildren().Append(obj);
7239
7240 lastPlainText = obj;
7241 }
7242 }
7243
7244 return lastPlainText;
7245 }
7246 }
7247 }
7248 return this;
7249}
7250
5d7836c4
JS
7251/// Dump to output stream for debugging
7252void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7253{
7254 wxRichTextObject::Dump(stream);
7255 stream << m_text << wxT("\n");
7256}
7257
ff76711f
JS
7258/// Get the first position from pos that has a line break character.
7259long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7260{
7261 int i;
7262 int len = m_text.length();
7263 int startPos = pos - m_range.GetStart();
7264 for (i = startPos; i < len; i++)
7265 {
7266 wxChar ch = m_text[i];
7267 if (ch == wxRichTextLineBreakChar)
7268 {
7269 return i + m_range.GetStart();
7270 }
7271 }
7272 return -1;
7273}
7274
5d7836c4
JS
7275/*!
7276 * wxRichTextBuffer
7277 * This is a kind of box, used to represent the whole buffer
7278 */
7279
7280IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7281
7c9fdebe
JS
7282wxList wxRichTextBuffer::sm_handlers;
7283wxList wxRichTextBuffer::sm_drawingHandlers;
7284wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7285wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7286int wxRichTextBuffer::sm_bulletRightMargin = 20;
7287float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
e12b91a3 7288bool wxRichTextBuffer::sm_floatingLayoutMode = true;
5d7836c4
JS
7289
7290/// Initialisation
7291void wxRichTextBuffer::Init()
7292{
7293 m_commandProcessor = new wxCommandProcessor;
7294 m_styleSheet = NULL;
7295 m_modified = false;
7296 m_batchedCommandDepth = 0;
7297 m_batchedCommand = NULL;
7298 m_suppressUndo = 0;
d2d0adc7 7299 m_handlerFlags = 0;
44219ff0 7300 m_scale = 1.0;
32423dd8
JS
7301 m_dimensionScale = 1.0;
7302 m_fontScale = 1.0;
f819ed5d 7303 SetMargins(4);
5d7836c4
JS
7304}
7305
7306/// Initialisation
7307wxRichTextBuffer::~wxRichTextBuffer()
7308{
7309 delete m_commandProcessor;
7310 delete m_batchedCommand;
7311
7312 ClearStyleStack();
d2d0adc7 7313 ClearEventHandlers();
5d7836c4
JS
7314}
7315
85d8909b 7316void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 7317{
85d8909b 7318 Reset();
3e541562 7319
5d7836c4 7320 GetCommandProcessor()->ClearCommands();
5d7836c4 7321
5d7836c4 7322 Modify(false);
1e967276 7323 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
7324}
7325
0ca07313
JS
7326void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7327{
7328 wxRichTextParagraphLayoutBox::Copy(obj);
7329
7330 m_styleSheet = obj.m_styleSheet;
7331 m_modified = obj.m_modified;
bec80f4f
JS
7332 m_batchedCommandDepth = 0;
7333 if (m_batchedCommand)
7334 delete m_batchedCommand;
7335 m_batchedCommand = NULL;
0ca07313 7336 m_suppressUndo = obj.m_suppressUndo;
603f702b 7337 m_invalidRange = obj.m_invalidRange;
32423dd8
JS
7338 m_dimensionScale = obj.m_dimensionScale;
7339 m_fontScale = obj.m_fontScale;
0ca07313
JS
7340}
7341
38f833b1
JS
7342/// Push style sheet to top of stack
7343bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7344{
7345 if (m_styleSheet)
7346 styleSheet->InsertSheet(m_styleSheet);
7347
7348 SetStyleSheet(styleSheet);
41a85215 7349
38f833b1
JS
7350 return true;
7351}
7352
7353/// Pop style sheet from top of stack
7354wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7355{
7356 if (m_styleSheet)
7357 {
7358 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7359 m_styleSheet = oldSheet->GetNextSheet();
7360 oldSheet->Unlink();
41a85215 7361
38f833b1
JS
7362 return oldSheet;
7363 }
7364 else
7365 return NULL;
7366}
7367
0ca07313
JS
7368/// Submit command to insert paragraphs
7369bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7370{
4e63bfb9 7371 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
7372}
7373
7374/// Submit command to insert paragraphs
4e63bfb9 7375bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
7376{
7377 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 7378
0ca07313 7379 action->GetNewParagraphs() = paragraphs;
59509217 7380
0ca07313
JS
7381 action->SetPosition(pos);
7382
603f702b 7383 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
7384 if (!paragraphs.GetPartialParagraph())
7385 range.SetEnd(range.GetEnd()+1);
7386
0ca07313 7387 // Set the range we'll need to delete in Undo
99404ab0 7388 action->SetRange(range);
0ca07313 7389
603f702b 7390 buffer->SubmitAction(action);
0ca07313
JS
7391
7392 return true;
7393}
7394
5d7836c4 7395/// Submit command to insert the given text
fe5aa22c 7396bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7397{
4e63bfb9 7398 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
7399}
7400
7401/// Submit command to insert the given text
4e63bfb9 7402bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7403{
7404 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7405
24777478
JS
7406 wxRichTextAttr* p = NULL;
7407 wxRichTextAttr paraAttr;
fe5aa22c
JS
7408 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7409 {
7c081bd2 7410 // Get appropriate paragraph style
603f702b 7411 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
7412 if (!paraAttr.IsDefault())
7413 p = & paraAttr;
7414 }
7415
fe5aa22c 7416 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 7417
603f702b 7418 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 7419
6636ef8d 7420 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
7421 {
7422 // Don't count the newline when undoing
7423 length --;
5d7836c4 7424 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 7425 }
6636ef8d 7426 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 7427 length --;
5d7836c4
JS
7428
7429 action->SetPosition(pos);
7430
7431 // Set the range we'll need to delete in Undo
0ca07313 7432 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 7433
603f702b 7434 buffer->SubmitAction(action);
7fe8059f 7435
5d7836c4
JS
7436 return true;
7437}
7438
7439/// Submit command to insert the given text
fe5aa22c 7440bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7441{
4e63bfb9 7442 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
7443}
7444
7445/// Submit command to insert the given text
4e63bfb9 7446bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7447{
7448 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7449
24777478
JS
7450 wxRichTextAttr* p = NULL;
7451 wxRichTextAttr paraAttr;
fe5aa22c
JS
7452 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7453 {
603f702b 7454 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
7455 if (!paraAttr.IsDefault())
7456 p = & paraAttr;
7457 }
7458
603f702b 7459 wxRichTextAttr attr(buffer->GetDefaultStyle());
32423dd8
JS
7460 // Don't include box attributes such as margins
7461 attr.GetTextBoxAttr().Reset();
7fe8059f
WS
7462
7463 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
7464 action->GetNewParagraphs().AppendChild(newPara);
7465 action->GetNewParagraphs().UpdateRanges();
7466 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
7467 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7468 long pos1 = pos;
7469
6c0ea513
JS
7470 if (p)
7471 newPara->SetAttributes(*p);
7472
c025e094
JS
7473 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7474 {
7475 if (para && para->GetRange().GetEnd() == pos)
7476 pos1 ++;
e2d0875a
JS
7477
7478 // Now see if we need to number the paragraph.
6c0ea513 7479 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
7480 {
7481 wxRichTextAttr numberingAttr;
7482 if (FindNextParagraphNumber(para, numberingAttr))
7483 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7484 }
c025e094
JS
7485 }
7486
5d7836c4
JS
7487 action->SetPosition(pos);
7488
99404ab0 7489 // Use the default character style
603f702b 7490 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
7491 {
7492 // Check whether the default style merely reflects the paragraph/basic style,
7493 // in which case don't apply it.
603f702b 7494 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
32423dd8 7495 defaultStyle.GetTextBoxAttr().Reset();
24777478 7496 wxRichTextAttr toApply;
c025e094
JS
7497 if (para)
7498 {
603f702b 7499 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 7500 wxRichTextAttr newAttr;
c025e094
JS
7501 // This filters out attributes that are accounted for by the current
7502 // paragraph/basic style
7503 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7504 }
7505 else
7506 toApply = defaultStyle;
7507
7508 if (!toApply.IsDefault())
7509 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7510 }
99404ab0 7511
5d7836c4 7512 // Set the range we'll need to delete in Undo
c025e094 7513 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 7514
603f702b 7515 buffer->SubmitAction(action);
7fe8059f 7516
5d7836c4
JS
7517 return true;
7518}
7519
7520/// Submit command to insert the given image
24777478
JS
7521bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7522 const wxRichTextAttr& textAttr)
5d7836c4 7523{
4e63bfb9 7524 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
7525}
7526
7527/// Submit command to insert the given image
4e63bfb9
JS
7528bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7529 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
7530 const wxRichTextAttr& textAttr)
7531{
7532 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7533
24777478
JS
7534 wxRichTextAttr* p = NULL;
7535 wxRichTextAttr paraAttr;
fe5aa22c
JS
7536 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7537 {
603f702b 7538 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
7539 if (!paraAttr.IsDefault())
7540 p = & paraAttr;
7541 }
7542
603f702b 7543 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 7544
32423dd8
JS
7545 // Don't include box attributes such as margins
7546 attr.GetTextBoxAttr().Reset();
7547
5d7836c4 7548 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
7549 if (p)
7550 newPara->SetAttributes(*p);
7551
5d7836c4
JS
7552 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7553 newPara->AppendChild(imageObject);
24777478 7554 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7555 action->GetNewParagraphs().AppendChild(newPara);
7556 action->GetNewParagraphs().UpdateRanges();
7557
7558 action->GetNewParagraphs().SetPartialParagraph(true);
7559
7560 action->SetPosition(pos);
7561
7562 // Set the range we'll need to delete in Undo
7563 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7564
603f702b 7565 buffer->SubmitAction(action);
7fe8059f 7566
5d7836c4
JS
7567 return true;
7568}
7569
cdaed652 7570// Insert an object with no change of it
603f702b
JS
7571wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7572{
4e63bfb9 7573 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7574}
7575
7576// Insert an object with no change of it
4e63bfb9 7577wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7578{
603f702b 7579 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7580
24777478
JS
7581 wxRichTextAttr* p = NULL;
7582 wxRichTextAttr paraAttr;
cdaed652
VZ
7583 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7584 {
603f702b 7585 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7586 if (!paraAttr.IsDefault())
7587 p = & paraAttr;
7588 }
7589
603f702b 7590 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652 7591
32423dd8
JS
7592 // Don't include box attributes such as margins
7593 attr.GetTextBoxAttr().Reset();
7594
cdaed652
VZ
7595 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7596 if (p)
7597 newPara->SetAttributes(*p);
7598
7599 newPara->AppendChild(object);
7600 action->GetNewParagraphs().AppendChild(newPara);
7601 action->GetNewParagraphs().UpdateRanges();
7602
7603 action->GetNewParagraphs().SetPartialParagraph(true);
7604
7605 action->SetPosition(pos);
7606
7607 // Set the range we'll need to delete in Undo
7608 action->SetRange(wxRichTextRange(pos, pos));
7609
603f702b 7610 buffer->SubmitAction(action);
cdaed652 7611
603f702b
JS
7612 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7613 return obj;
cdaed652 7614}
603f702b 7615
7c9fdebe
JS
7616wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7617 const wxRichTextProperties& properties,
7618 wxRichTextCtrl* ctrl, int flags,
7619 const wxRichTextAttr& textAttr)
7620{
7621 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7622
7623 wxRichTextAttr* p = NULL;
7624 wxRichTextAttr paraAttr;
7625 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7626 {
7627 paraAttr = GetStyleForNewParagraph(buffer, pos);
7628 if (!paraAttr.IsDefault())
7629 p = & paraAttr;
7630 }
7631
7632 wxRichTextAttr attr(buffer->GetDefaultStyle());
7633
32423dd8
JS
7634 // Don't include box attributes such as margins
7635 attr.GetTextBoxAttr().Reset();
7636
7c9fdebe
JS
7637 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7638 if (p)
7639 newPara->SetAttributes(*p);
7640
7641 wxRichTextField* fieldObject = new wxRichTextField();
7642 fieldObject->wxRichTextObject::SetProperties(properties);
7643 fieldObject->SetFieldType(fieldType);
7644 fieldObject->SetAttributes(textAttr);
7645 newPara->AppendChild(fieldObject);
7646 action->GetNewParagraphs().AppendChild(newPara);
7647 action->GetNewParagraphs().UpdateRanges();
7648 action->GetNewParagraphs().SetPartialParagraph(true);
7649 action->SetPosition(pos);
7650
7651 // Set the range we'll need to delete in Undo
7652 action->SetRange(wxRichTextRange(pos, pos));
7653
7654 buffer->SubmitAction(action);
7655
7656 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7657 return obj;
7658}
7659
fe5aa22c
JS
7660/// Get the style that is appropriate for a new paragraph at this position.
7661/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7662/// style.
603f702b 7663wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7664{
7665 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7666 if (para)
7667 {
24777478 7668 wxRichTextAttr attr;
d2d0adc7 7669 bool foundAttributes = false;
3e541562 7670
d2d0adc7 7671 // Look for a matching paragraph style
603f702b 7672 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7673 {
603f702b 7674 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7675 if (paraDef)
fe5aa22c 7676 {
caad0109
JS
7677 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7678 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7679 {
603f702b 7680 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7681 if (nextParaDef)
7682 {
7683 foundAttributes = true;
603f702b 7684 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7685 }
7686 }
3e541562 7687
d2d0adc7
JS
7688 // If we didn't find the 'next style', use this style instead.
7689 if (!foundAttributes)
7690 {
7691 foundAttributes = true;
603f702b 7692 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7693 }
fe5aa22c
JS
7694 }
7695 }
e2d0875a
JS
7696
7697 // Also apply list style if present
603f702b 7698 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7699 {
603f702b 7700 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7701 if (listDef)
7702 {
7703 int thisIndent = para->GetAttributes().GetLeftIndent();
7704 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7705
7706 // Apply the overall list style, and item style for this level
603f702b 7707 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7708 wxRichTextApplyStyle(attr, listStyle);
7709 attr.SetOutlineLevel(thisLevel);
7710 if (para->GetAttributes().HasBulletNumber())
7711 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7712 }
34b4899d 7713 }
e2d0875a 7714
d2d0adc7
JS
7715 if (!foundAttributes)
7716 {
7717 attr = para->GetAttributes();
7718 int flags = attr.GetFlags();
fe5aa22c 7719
d2d0adc7
JS
7720 // Eliminate character styles
7721 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7722 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7723 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7724 attr.SetFlags(flags);
7725 }
3e541562 7726
fe5aa22c
JS
7727 return attr;
7728 }
7729 else
24777478 7730 return wxRichTextAttr();
fe5aa22c
JS
7731}
7732
5d7836c4 7733/// Submit command to delete this range
12cc29c5 7734bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7735{
603f702b
JS
7736 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7737}
7738
7739/// Submit command to delete this range
7740bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7741{
7742 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7743
12cc29c5 7744 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7745
7746 // Set the range to delete
7747 action->SetRange(range);
7fe8059f 7748
5d7836c4
JS
7749 // Copy the fragment that we'll need to restore in Undo
7750 CopyFragment(range, action->GetOldParagraphs());
7751
6c0ea513
JS
7752 // See if we're deleting a paragraph marker, in which case we need to
7753 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7754 if (range.GetStart() == range.GetEnd())
5d7836c4 7755 {
6c0ea513
JS
7756 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7757 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7758 {
6c0ea513
JS
7759 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7760 if (nextPara && nextPara != para)
5d7836c4 7761 {
6c0ea513
JS
7762 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7763 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7764 }
7765 }
7766 }
7767
603f702b 7768 buffer->SubmitAction(action);
7fe8059f 7769
5d7836c4
JS
7770 return true;
7771}
7772
7773/// Collapse undo/redo commands
7774bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7775{
7776 if (m_batchedCommandDepth == 0)
7777 {
7778 wxASSERT(m_batchedCommand == NULL);
7779 if (m_batchedCommand)
7780 {
0745f364 7781 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7782 }
7783 m_batchedCommand = new wxRichTextCommand(cmdName);
7784 }
7785
7fe8059f 7786 m_batchedCommandDepth ++;
5d7836c4
JS
7787
7788 return true;
7789}
7790
7791/// Collapse undo/redo commands
7792bool wxRichTextBuffer::EndBatchUndo()
7793{
7794 m_batchedCommandDepth --;
7795
7796 wxASSERT(m_batchedCommandDepth >= 0);
7797 wxASSERT(m_batchedCommand != NULL);
7798
7799 if (m_batchedCommandDepth == 0)
7800 {
0745f364 7801 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7802 m_batchedCommand = NULL;
7803 }
7804
7805 return true;
7806}
7807
7808/// Submit immediately, or delay according to whether collapsing is on
7809bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7810{
cc2aecde
JS
7811 if (action && !action->GetNewParagraphs().IsEmpty())
7812 PrepareContent(action->GetNewParagraphs());
7813
5d7836c4 7814 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7815 {
7816 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7817 cmd->AddAction(action);
7818 cmd->Do();
7819 cmd->GetActions().Clear();
7820 delete cmd;
7821
5d7836c4 7822 m_batchedCommand->AddAction(action);
0745f364 7823 }
5d7836c4
JS
7824 else
7825 {
7826 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7827 cmd->AddAction(action);
7828
7829 // Only store it if we're not suppressing undo.
7830 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7831 }
7832
7833 return true;
7834}
7835
7836/// Begin suppressing undo/redo commands.
7837bool wxRichTextBuffer::BeginSuppressUndo()
7838{
7fe8059f 7839 m_suppressUndo ++;
5d7836c4
JS
7840
7841 return true;
7842}
7843
7844/// End suppressing undo/redo commands.
7845bool wxRichTextBuffer::EndSuppressUndo()
7846{
7fe8059f 7847 m_suppressUndo --;
5d7836c4
JS
7848
7849 return true;
7850}
7851
7852/// Begin using a style
24777478 7853bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7854{
24777478 7855 wxRichTextAttr newStyle(GetDefaultStyle());
32423dd8 7856 newStyle.GetTextBoxAttr().Reset();
5d7836c4
JS
7857
7858 // Save the old default style
32423dd8 7859 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
5d7836c4
JS
7860
7861 wxRichTextApplyStyle(newStyle, style);
7862 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7863
7864 SetDefaultStyle(newStyle);
7865
5d7836c4
JS
7866 return true;
7867}
7868
7869/// End the style
7870bool wxRichTextBuffer::EndStyle()
7871{
63886f6d 7872 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7873 {
7874 wxLogDebug(_("Too many EndStyle calls!"));
7875 return false;
7876 }
7877
09f14108 7878 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7879 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7880 m_attributeStack.Erase(node);
5d7836c4
JS
7881
7882 SetDefaultStyle(*attr);
7883
7884 delete attr;
7885 return true;
7886}
7887
7888/// End all styles
7889bool wxRichTextBuffer::EndAllStyles()
7890{
7891 while (m_attributeStack.GetCount() != 0)
7892 EndStyle();
7893 return true;
7894}
7895
7896/// Clear the style stack
7897void wxRichTextBuffer::ClearStyleStack()
7898{
09f14108 7899 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7900 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7901 m_attributeStack.Clear();
7902}
7903
7904/// Begin using bold
7905bool wxRichTextBuffer::BeginBold()
7906{
24777478 7907 wxRichTextAttr attr;
7d76fbd5 7908 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7909
5d7836c4
JS
7910 return BeginStyle(attr);
7911}
7912
7913/// Begin using italic
7914bool wxRichTextBuffer::BeginItalic()
7915{
24777478 7916 wxRichTextAttr attr;
7d76fbd5 7917 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7918
5d7836c4
JS
7919 return BeginStyle(attr);
7920}
7921
7922/// Begin using underline
7923bool wxRichTextBuffer::BeginUnderline()
7924{
24777478 7925 wxRichTextAttr attr;
44cc96a8 7926 attr.SetFontUnderlined(true);
7fe8059f 7927
5d7836c4
JS
7928 return BeginStyle(attr);
7929}
7930
7931/// Begin using point size
7932bool wxRichTextBuffer::BeginFontSize(int pointSize)
7933{
24777478 7934 wxRichTextAttr attr;
44cc96a8 7935 attr.SetFontSize(pointSize);
7fe8059f 7936
5d7836c4
JS
7937 return BeginStyle(attr);
7938}
7939
7940/// Begin using this font
7941bool wxRichTextBuffer::BeginFont(const wxFont& font)
7942{
24777478 7943 wxRichTextAttr attr;
5d7836c4 7944 attr.SetFont(font);
7fe8059f 7945
5d7836c4
JS
7946 return BeginStyle(attr);
7947}
7948
7949/// Begin using this colour
7950bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7951{
24777478 7952 wxRichTextAttr attr;
5d7836c4
JS
7953 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7954 attr.SetTextColour(colour);
7fe8059f 7955
5d7836c4
JS
7956 return BeginStyle(attr);
7957}
7958
7959/// Begin using alignment
7960bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7961{
24777478 7962 wxRichTextAttr attr;
5d7836c4
JS
7963 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7964 attr.SetAlignment(alignment);
7fe8059f 7965
5d7836c4
JS
7966 return BeginStyle(attr);
7967}
7968
7969/// Begin left indent
7970bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7971{
24777478 7972 wxRichTextAttr attr;
5d7836c4
JS
7973 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7974 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7975
5d7836c4
JS
7976 return BeginStyle(attr);
7977}
7978
7979/// Begin right indent
7980bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7981{
24777478 7982 wxRichTextAttr attr;
5d7836c4
JS
7983 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7984 attr.SetRightIndent(rightIndent);
7fe8059f 7985
5d7836c4
JS
7986 return BeginStyle(attr);
7987}
7988
7989/// Begin paragraph spacing
7990bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7991{
7992 long flags = 0;
7993 if (before != 0)
7994 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7995 if (after != 0)
7996 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7997
24777478 7998 wxRichTextAttr attr;
5d7836c4
JS
7999 attr.SetFlags(flags);
8000 attr.SetParagraphSpacingBefore(before);
8001 attr.SetParagraphSpacingAfter(after);
7fe8059f 8002
5d7836c4
JS
8003 return BeginStyle(attr);
8004}
8005
8006/// Begin line spacing
8007bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8008{
24777478 8009 wxRichTextAttr attr;
5d7836c4
JS
8010 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8011 attr.SetLineSpacing(lineSpacing);
7fe8059f 8012
5d7836c4
JS
8013 return BeginStyle(attr);
8014}
8015
8016/// Begin numbered bullet
8017bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8018{
24777478 8019 wxRichTextAttr attr;
f089713f 8020 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8021 attr.SetBulletStyle(bulletStyle);
8022 attr.SetBulletNumber(bulletNumber);
8023 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8024
5d7836c4
JS
8025 return BeginStyle(attr);
8026}
8027
8028/// Begin symbol bullet
d2d0adc7 8029bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 8030{
24777478 8031 wxRichTextAttr attr;
f089713f 8032 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8033 attr.SetBulletStyle(bulletStyle);
8034 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 8035 attr.SetBulletText(symbol);
7fe8059f 8036
5d7836c4
JS
8037 return BeginStyle(attr);
8038}
8039
f089713f
JS
8040/// Begin standard bullet
8041bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8042{
24777478 8043 wxRichTextAttr attr;
f089713f
JS
8044 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8045 attr.SetBulletStyle(bulletStyle);
8046 attr.SetLeftIndent(leftIndent, leftSubIndent);
8047 attr.SetBulletName(bulletName);
8048
8049 return BeginStyle(attr);
8050}
8051
5d7836c4
JS
8052/// Begin named character style
8053bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8054{
8055 if (GetStyleSheet())
8056 {
8057 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8058 if (def)
8059 {
24777478 8060 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8061 return BeginStyle(attr);
8062 }
8063 }
8064 return false;
8065}
8066
8067/// Begin named paragraph style
8068bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8069{
8070 if (GetStyleSheet())
8071 {
8072 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8073 if (def)
8074 {
24777478 8075 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8076 return BeginStyle(attr);
8077 }
8078 }
8079 return false;
8080}
8081
f089713f
JS
8082/// Begin named list style
8083bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8084{
8085 if (GetStyleSheet())
8086 {
8087 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8088 if (def)
8089 {
24777478 8090 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
8091
8092 attr.SetBulletNumber(number);
8093
8094 return BeginStyle(attr);
8095 }
8096 }
8097 return false;
8098}
8099
d2d0adc7
JS
8100/// Begin URL
8101bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8102{
24777478 8103 wxRichTextAttr attr;
d2d0adc7
JS
8104
8105 if (!characterStyle.IsEmpty() && GetStyleSheet())
8106 {
8107 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8108 if (def)
8109 {
336d8ae9 8110 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
8111 }
8112 }
8113 attr.SetURL(url);
8114
8115 return BeginStyle(attr);
8116}
8117
5d7836c4
JS
8118/// Adds a handler to the end
8119void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8120{
8121 sm_handlers.Append(handler);
8122}
8123
8124/// Inserts a handler at the front
8125void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8126{
8127 sm_handlers.Insert( handler );
8128}
8129
8130/// Removes a handler
8131bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8132{
8133 wxRichTextFileHandler *handler = FindHandler(name);
8134 if (handler)
8135 {
8136 sm_handlers.DeleteObject(handler);
8137 delete handler;
8138 return true;
8139 }
8140 else
8141 return false;
8142}
8143
8144/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
8145wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8146 wxRichTextFileType imageType)
5d7836c4
JS
8147{
8148 if (imageType != wxRICHTEXT_TYPE_ANY)
8149 return FindHandler(imageType);
0ca07313 8150 else if (!filename.IsEmpty())
5d7836c4
JS
8151 {
8152 wxString path, file, ext;
a51e601e 8153 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
8154 return FindHandler(ext, imageType);
8155 }
0ca07313
JS
8156 else
8157 return NULL;
5d7836c4
JS
8158}
8159
8160
8161/// Finds a handler by name
8162wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8163{
8164 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8165 while (node)
8166 {
8167 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8168 if (handler->GetName().Lower() == name.Lower()) return handler;
8169
8170 node = node->GetNext();
8171 }
8172 return NULL;
8173}
8174
8175/// Finds a handler by extension and type
d75a69e8 8176wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
8177{
8178 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8179 while (node)
8180 {
8181 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8182 if ( handler->GetExtension().Lower() == extension.Lower() &&
8183 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8184 return handler;
8185 node = node->GetNext();
8186 }
8187 return 0;
8188}
8189
8190/// Finds a handler by type
d75a69e8 8191wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
8192{
8193 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8194 while (node)
8195 {
8196 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8197 if (handler->GetType() == type) return handler;
8198 node = node->GetNext();
8199 }
8200 return NULL;
8201}
8202
8203void wxRichTextBuffer::InitStandardHandlers()
8204{
8205 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8206 AddHandler(new wxRichTextPlainTextHandler);
8207}
8208
8209void wxRichTextBuffer::CleanUpHandlers()
8210{
8211 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8212 while (node)
8213 {
8214 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8215 wxList::compatibility_iterator next = node->GetNext();
8216 delete handler;
8217 node = next;
8218 }
8219
8220 sm_handlers.Clear();
8221}
8222
1e967276 8223wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 8224{
1e967276
JS
8225 if (types)
8226 types->Clear();
8227
5d7836c4
JS
8228 wxString wildcard;
8229
8230 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8231 int count = 0;
8232 while (node)
8233 {
8234 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 8235 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
8236 {
8237 if (combine)
8238 {
8239 if (count > 0)
8240 wildcard += wxT(";");
8241 wildcard += wxT("*.") + handler->GetExtension();
8242 }
8243 else
8244 {
8245 if (count > 0)
8246 wildcard += wxT("|");
8247 wildcard += handler->GetName();
8248 wildcard += wxT(" ");
8249 wildcard += _("files");
8250 wildcard += wxT(" (*.");
8251 wildcard += handler->GetExtension();
8252 wildcard += wxT(")|*.");
8253 wildcard += handler->GetExtension();
1e967276
JS
8254 if (types)
8255 types->Add(handler->GetType());
5d7836c4
JS
8256 }
8257 count ++;
8258 }
8259
8260 node = node->GetNext();
8261 }
8262
8263 if (combine)
8264 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8265 return wildcard;
8266}
8267
8268/// Load a file
d75a69e8 8269bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8270{
8271 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8272 if (handler)
1e967276 8273 {
24777478 8274 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8275 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8276 bool success = handler->LoadFile(this, filename);
8277 Invalidate(wxRICHTEXT_ALL);
8278 return success;
8279 }
5d7836c4
JS
8280 else
8281 return false;
8282}
8283
8284/// Save a file
d75a69e8 8285bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8286{
8287 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8288 if (handler)
d2d0adc7
JS
8289 {
8290 handler->SetFlags(GetHandlerFlags());
5d7836c4 8291 return handler->SaveFile(this, filename);
d2d0adc7 8292 }
5d7836c4
JS
8293 else
8294 return false;
8295}
8296
8297/// Load from a stream
d75a69e8 8298bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8299{
8300 wxRichTextFileHandler* handler = FindHandler(type);
8301 if (handler)
1e967276 8302 {
24777478 8303 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8304 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8305 bool success = handler->LoadFile(this, stream);
8306 Invalidate(wxRICHTEXT_ALL);
8307 return success;
8308 }
5d7836c4
JS
8309 else
8310 return false;
8311}
8312
8313/// Save to a stream
d75a69e8 8314bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8315{
8316 wxRichTextFileHandler* handler = FindHandler(type);
8317 if (handler)
d2d0adc7
JS
8318 {
8319 handler->SetFlags(GetHandlerFlags());
5d7836c4 8320 return handler->SaveFile(this, stream);
d2d0adc7 8321 }
5d7836c4
JS
8322 else
8323 return false;
8324}
8325
8326/// Copy the range to the clipboard
8327bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8328{
8329 bool success = false;
603f702b
JS
8330 wxRichTextParagraphLayoutBox* container = this;
8331 if (GetRichTextCtrl())
8332 container = GetRichTextCtrl()->GetFocusObject();
8333
11ef729d 8334#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 8335
d2142335 8336 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 8337 {
0ca07313
JS
8338 wxTheClipboard->Clear();
8339
8340 // Add composite object
8341
8342 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8343
8344 {
603f702b 8345 wxString text = container->GetTextForRange(range);
0ca07313
JS
8346
8347#ifdef __WXMSW__
8348 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8349#endif
8350
8351 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8352 }
8353
8354 // Add rich text buffer data object. This needs the XML handler to be present.
8355
8356 if (FindHandler(wxRICHTEXT_TYPE_XML))
8357 {
8358 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 8359 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
8360
8361 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8362 }
8363
8364 if (wxTheClipboard->SetData(compositeObject))
8365 success = true;
8366
5d7836c4
JS
8367 wxTheClipboard->Close();
8368 }
0ca07313 8369
39a1c2f2
WS
8370#else
8371 wxUnusedVar(range);
8372#endif
5d7836c4
JS
8373 return success;
8374}
8375
8376/// Paste the clipboard content to the buffer
8377bool wxRichTextBuffer::PasteFromClipboard(long position)
8378{
8379 bool success = false;
603f702b
JS
8380 wxRichTextParagraphLayoutBox* container = this;
8381 if (GetRichTextCtrl())
8382 container = GetRichTextCtrl()->GetFocusObject();
8383
11ef729d 8384#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
8385 if (CanPasteFromClipboard())
8386 {
8387 if (wxTheClipboard->Open())
8388 {
0ca07313
JS
8389 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8390 {
8391 wxRichTextBufferDataObject data;
8392 wxTheClipboard->GetData(data);
8393 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8394 if (richTextBuffer)
8395 {
4e63bfb9 8396 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 8397 if (GetRichTextCtrl())
603f702b 8398 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
8399 delete richTextBuffer;
8400 }
8401 }
f7d83f24 8402 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
8403 #if wxUSE_UNICODE
8404 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8405 #endif
f7d83f24 8406 )
5d7836c4
JS
8407 {
8408 wxTextDataObject data;
8409 wxTheClipboard->GetData(data);
8410 wxString text(data.GetText());
c21f3211
JS
8411#ifdef __WXMSW__
8412 wxString text2;
8413 text2.Alloc(text.Length()+1);
8414 size_t i;
8415 for (i = 0; i < text.Length(); i++)
8416 {
8417 wxChar ch = text[i];
8418 if (ch != wxT('\r'))
8419 text2 += ch;
8420 }
8421#else
8422 wxString text2 = text;
8423#endif
4e63bfb9 8424 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 8425
62381daa
JS
8426 if (GetRichTextCtrl())
8427 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8428
5d7836c4
JS
8429 success = true;
8430 }
8431 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8432 {
8433 wxBitmapDataObject data;
8434 wxTheClipboard->GetData(data);
8435 wxBitmap bitmap(data.GetBitmap());
8436 wxImage image(bitmap.ConvertToImage());
8437
603f702b 8438 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 8439
5d7836c4
JS
8440 action->GetNewParagraphs().AddImage(image);
8441
8442 if (action->GetNewParagraphs().GetChildCount() == 1)
8443 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 8444
9c8e10ad 8445 action->SetPosition(position+1);
7fe8059f 8446
5d7836c4 8447 // Set the range we'll need to delete in Undo
9c8e10ad 8448 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 8449
5d7836c4
JS
8450 SubmitAction(action);
8451
8452 success = true;
8453 }
8454 wxTheClipboard->Close();
8455 }
8456 }
39a1c2f2
WS
8457#else
8458 wxUnusedVar(position);
8459#endif
5d7836c4
JS
8460 return success;
8461}
8462
8463/// Can we paste from the clipboard?
8464bool wxRichTextBuffer::CanPasteFromClipboard() const
8465{
7fe8059f 8466 bool canPaste = false;
11ef729d 8467#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 8468 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 8469 {
f7d83f24
VZ
8470 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8471#if wxUSE_UNICODE
603f702b
JS
8472 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8473#endif
8474 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8475 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 8476 {
7fe8059f 8477 canPaste = true;
5d7836c4
JS
8478 }
8479 wxTheClipboard->Close();
8480 }
39a1c2f2 8481#endif
5d7836c4
JS
8482 return canPaste;
8483}
8484
8485/// Dumps contents of buffer for debugging purposes
8486void wxRichTextBuffer::Dump()
8487{
8488 wxString text;
8489 {
8490 wxStringOutputStream stream(& text);
8491 wxTextOutputStream textStream(stream);
8492 Dump(textStream);
8493 }
8494
8495 wxLogDebug(text);
8496}
8497
d2d0adc7
JS
8498/// Add an event handler
8499bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8500{
8501 m_eventHandlers.Append(handler);
8502 return true;
8503}
8504
8505/// Remove an event handler
8506bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8507{
8508 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8509 if (node)
8510 {
8511 m_eventHandlers.Erase(node);
8512 if (deleteHandler)
8513 delete handler;
3e541562 8514
d2d0adc7
JS
8515 return true;
8516 }
8517 else
8518 return false;
8519}
8520
8521/// Clear event handlers
8522void wxRichTextBuffer::ClearEventHandlers()
8523{
8524 m_eventHandlers.Clear();
8525}
8526
8527/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8528/// otherwise will stop at the first successful one.
8529bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8530{
8531 bool success = false;
8532 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8533 {
8534 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8535 if (handler->ProcessEvent(event))
8536 {
8537 success = true;
8538 if (!sendToAll)
8539 return true;
8540 }
8541 }
8542 return success;
8543}
8544
8545/// Set style sheet and notify of the change
8546bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8547{
8548 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 8549
616c7cbd 8550 wxWindowID winid = wxID_ANY;
d2d0adc7 8551 if (GetRichTextCtrl())
616c7cbd 8552 winid = GetRichTextCtrl()->GetId();
3e541562 8553
616c7cbd 8554 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 8555 event.SetEventObject(GetRichTextCtrl());
603f702b 8556 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
8557 event.SetOldStyleSheet(oldSheet);
8558 event.SetNewStyleSheet(sheet);
8559 event.Allow();
3e541562 8560
d2d0adc7
JS
8561 if (SendEvent(event) && !event.IsAllowed())
8562 {
8563 if (sheet != oldSheet)
8564 delete sheet;
8565
8566 return false;
8567 }
8568
8569 if (oldSheet && oldSheet != sheet)
8570 delete oldSheet;
8571
8572 SetStyleSheet(sheet);
8573
8574 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8575 event.SetOldStyleSheet(NULL);
8576 event.Allow();
8577
8578 return SendEvent(event);
8579}
8580
8581/// Set renderer, deleting old one
8582void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8583{
8584 if (sm_renderer)
8585 delete sm_renderer;
8586 sm_renderer = renderer;
8587}
8588
603f702b
JS
8589/// Hit-testing: returns a flag indicating hit test details, plus
8590/// information about position
8db2e3ef 8591int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 8592{
8db2e3ef 8593 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
8594 if (ret != wxRICHTEXT_HITTEST_NONE)
8595 {
8596 return ret;
8597 }
8598 else
8599 {
8600 textPosition = m_ownRange.GetEnd()-1;
8601 *obj = this;
8602 *contextObj = this;
8603 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8604 }
8605}
8606
32423dd8
JS
8607void wxRichTextBuffer::SetFontScale(double fontScale)
8608{
8609 m_fontScale = fontScale;
8610 m_fontTable.SetFontScale(fontScale);
8611}
8612
8613void wxRichTextBuffer::SetDimensionScale(double dimScale)
8614{
8615 m_dimensionScale = dimScale;
8616}
8617
24777478 8618bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8619{
a1b806b9 8620 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8621 {
ecb5fbf1
JS
8622 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8623 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8624 }
8625 else
8626 {
ecb5fbf1
JS
8627 wxCheckSetPen(dc, *wxBLACK_PEN);
8628 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8629 }
8630
8631 wxFont font;
44cc96a8
JS
8632 if (bulletAttr.HasFont())
8633 {
8634 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8635 }
d2d0adc7
JS
8636 else
8637 font = (*wxNORMAL_FONT);
8638
ecb5fbf1 8639 wxCheckSetFont(dc, font);
d2d0adc7
JS
8640
8641 int charHeight = dc.GetCharHeight();
3e541562 8642
d2d0adc7
JS
8643 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8644 int bulletHeight = bulletWidth;
8645
8646 int x = rect.x;
3e541562 8647
d2d0adc7
JS
8648 // Calculate the top position of the character (as opposed to the whole line height)
8649 int y = rect.y + (rect.height - charHeight);
3e541562 8650
d2d0adc7
JS
8651 // Calculate where the bullet should be positioned
8652 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8653
d2d0adc7 8654 // The margin between a bullet and text.
44219ff0 8655 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8656
d2d0adc7
JS
8657 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8658 x = rect.x + rect.width - bulletWidth - margin;
8659 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8660 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8661
d2d0adc7
JS
8662 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8663 {
8664 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8665 }
8666 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8667 {
8668 wxPoint pts[5];
8669 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8670 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8671 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8672 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8673
d2d0adc7
JS
8674 dc.DrawPolygon(4, pts);
8675 }
8676 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8677 {
8678 wxPoint pts[3];
8679 pts[0].x = x; pts[0].y = y;
8680 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8681 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8682
d2d0adc7
JS
8683 dc.DrawPolygon(3, pts);
8684 }
603f702b
JS
8685 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8686 {
8687 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8688 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8689 }
d2d0adc7
JS
8690 else // "standard/circle", and catch-all
8691 {
8692 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8693 }
8694
d2d0adc7
JS
8695 return true;
8696}
8697
24777478 8698bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8699{
8700 if (!text.empty())
8701 {
8702 wxFont font;
44cc96a8
JS
8703 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8704 {
24777478 8705 wxRichTextAttr fontAttr;
32423dd8
JS
8706 if (attr.HasFontPixelSize())
8707 fontAttr.SetFontPixelSize(attr.GetFontSize());
8708 else
8709 fontAttr.SetFontPointSize(attr.GetFontSize());
44cc96a8
JS
8710 fontAttr.SetFontStyle(attr.GetFontStyle());
8711 fontAttr.SetFontWeight(attr.GetFontWeight());
8712 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8713 fontAttr.SetFontFaceName(attr.GetBulletFont());
8714 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8715 }
8716 else if (attr.HasFont())
8717 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8718 else
8719 font = (*wxNORMAL_FONT);
8720
ecb5fbf1 8721 wxCheckSetFont(dc, font);
d2d0adc7 8722
a1b806b9 8723 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8724 dc.SetTextForeground(attr.GetTextColour());
8725
04ee05f9 8726 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8727
8728 int charHeight = dc.GetCharHeight();
8729 wxCoord tw, th;
8730 dc.GetTextExtent(text, & tw, & th);
8731
8732 int x = rect.x;
8733
8734 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8735 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8736
8737 // The margin between a bullet and text.
44219ff0 8738 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8739
d2d0adc7
JS
8740 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8741 x = (rect.x + rect.width) - tw - margin;
8742 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8743 x = x + (rect.width)/2 - tw/2;
8744
8745 dc.DrawText(text, x, y);
3e541562 8746
d2d0adc7
JS
8747 return true;
8748 }
8749 else
8750 return false;
8751}
8752
24777478 8753bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8754{
8755 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8756 // with the buffer. The store will allow retrieval from memory, disk or other means.
8757 return false;
8758}
8759
8760/// Enumerate the standard bullet names currently supported
8761bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8762{
04529b2a 8763 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8764 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8765 bulletNames.Add(wxTRANSLATE("standard/square"));
8766 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8767 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8768
8769 return true;
8770}
5d7836c4 8771
bec80f4f
JS
8772/*!
8773 * wxRichTextBox
8774 */
8775
603f702b 8776IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8777
8778wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8779 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8780{
8781}
8782
8783/// Draw the item
8db2e3ef 8784bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8785{
603f702b
JS
8786 if (!IsShown())
8787 return true;
5ad9ae3a 8788
603f702b
JS
8789 // TODO: if the active object in the control, draw an indication.
8790 // We need to add the concept of active object, and not just focus object,
8791 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8792 // Ultimately we would like to be able to interactively resize an active object
8793 // using drag handles.
8db2e3ef 8794 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8795}
5ad9ae3a 8796
603f702b
JS
8797/// Copy
8798void wxRichTextBox::Copy(const wxRichTextBox& obj)
8799{
8800 wxRichTextParagraphLayoutBox::Copy(obj);
8801}
8802
8803// Edit properties via a GUI
8804bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8805{
8806 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8807 boxDlg.SetAttributes(GetAttributes());
8808
8809 if (boxDlg.ShowModal() == wxID_OK)
8810 {
8811 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8812 // indeterminate in the object.
8813 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8814 return true;
5ad9ae3a 8815 }
603f702b
JS
8816 else
8817 return false;
bec80f4f
JS
8818}
8819
7c9fdebe
JS
8820/*!
8821 * wxRichTextField
8822 */
8823
8824IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8825
8826wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8827 wxRichTextParagraphLayoutBox(parent)
8828{
8829 SetFieldType(fieldType);
8830}
8831
8832/// Draw the item
8833bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8834{
8835 if (!IsShown())
8836 return true;
8837
8838 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8839 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8840 return true;
8841
8842 // Fallback; but don't draw guidelines.
8843 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8844 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8845}
8846
8847bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8848{
8849 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8850 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8851 return true;
8852
8853 // Fallback
8854 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8855}
8856
8857bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8858{
8859 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8860 if (fieldType)
8861 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents);
8862
8863 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
8864}
8865
8866/// Calculate range
8867void wxRichTextField::CalculateRange(long start, long& end)
8868{
8869 if (IsTopLevel())
8870 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8871 else
8872 wxRichTextObject::CalculateRange(start, end);
8873}
8874
8875/// Copy
8876void wxRichTextField::Copy(const wxRichTextField& obj)
8877{
8878 wxRichTextParagraphLayoutBox::Copy(obj);
8879
32423dd8 8880 UpdateField(GetBuffer());
7c9fdebe
JS
8881}
8882
8883// Edit properties via a GUI
8884bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8885{
8886 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8887 if (fieldType)
8888 return fieldType->EditProperties(this, parent, buffer);
8889
8890 return false;
8891}
8892
8893bool wxRichTextField::CanEditProperties() const
8894{
8895 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8896 if (fieldType)
8897 return fieldType->CanEditProperties((wxRichTextField*) this);
8898
8899 return false;
8900}
8901
8902wxString wxRichTextField::GetPropertiesMenuLabel() const
8903{
8904 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8905 if (fieldType)
8906 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8907
8908 return wxEmptyString;
8909}
8910
32423dd8 8911bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
7c9fdebe
JS
8912{
8913 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8914 if (fieldType)
32423dd8 8915 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
7c9fdebe
JS
8916
8917 return false;
8918}
8919
8920bool wxRichTextField::IsTopLevel() const
8921{
8922 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8923 if (fieldType)
8924 return fieldType->IsTopLevel((wxRichTextField*) this);
8925
8926 return true;
8927}
8928
8929IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8930
8931IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8932
8933wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8934{
8935 Init();
8936
8937 SetName(name);
8938 SetLabel(label);
8939 SetDisplayStyle(displayStyle);
8940}
8941
8942wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8943{
8944 Init();
8945
8946 SetName(name);
8947 SetBitmap(bitmap);
8948 SetDisplayStyle(displayStyle);
8949}
8950
8951void wxRichTextFieldTypeStandard::Init()
8952{
8953 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8954 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8955 m_textColour = *wxWHITE;
8956 m_borderColour = *wxBLACK;
8957 m_backgroundColour = *wxBLACK;
8958 m_verticalPadding = 1;
8959 m_horizontalPadding = 3;
8960 m_horizontalMargin = 2;
8961 m_verticalMargin = 0;
8962}
8963
8964void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
8965{
8966 wxRichTextFieldType::Copy(field);
8967
8968 m_label = field.m_label;
8969 m_displayStyle = field.m_displayStyle;
8970 m_font = field.m_font;
8971 m_textColour = field.m_textColour;
8972 m_borderColour = field.m_borderColour;
8973 m_backgroundColour = field.m_backgroundColour;
8974 m_verticalPadding = field.m_verticalPadding;
8975 m_horizontalPadding = field.m_horizontalPadding;
8976 m_horizontalMargin = field.m_horizontalMargin;
8977 m_bitmap = field.m_bitmap;
8978}
8979
8980bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
8981{
8982 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8983 return false; // USe default composite drawing
8984 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8985 {
8986 int borderSize = 1;
8987
8988 wxPen borderPen(m_borderColour, 1, wxSOLID);
8989 wxBrush backgroundBrush(m_backgroundColour);
8990 wxColour textColour(m_textColour);
8991
8992 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8993 {
8994 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
8995 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
8996
8997 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
8998 backgroundBrush = wxBrush(highlightColour);
8999
9000 wxCheckSetBrush(dc, backgroundBrush);
9001 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9002 dc.DrawRectangle(rect);
9003 }
9004
9005 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9006 borderSize = 0;
9007
9008 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9009 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9010 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9011
9012 // clientArea is where the text is actually written
9013 wxRect clientArea = objectRect;
9014
9015 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9016 {
9017 dc.SetPen(borderPen);
9018 dc.SetBrush(backgroundBrush);
9019 dc.DrawRoundedRectangle(objectRect, 4.0);
9020 }
9021 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9022 {
9023 int arrowLength = objectRect.height/2;
9024 clientArea.width -= (arrowLength - m_horizontalPadding);
9025
9026 wxPoint pts[5];
9027 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9028 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9029 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9030 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9031 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9032 dc.SetPen(borderPen);
9033 dc.SetBrush(backgroundBrush);
9034 dc.DrawPolygon(5, pts);
9035 }
9036 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9037 {
9038 int arrowLength = objectRect.height/2;
9039 clientArea.width -= (arrowLength - m_horizontalPadding);
9040 clientArea.x += (arrowLength - m_horizontalPadding);
9041
9042 wxPoint pts[5];
9043 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9044 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9045 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9046 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9047 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9048 dc.SetPen(borderPen);
9049 dc.SetBrush(backgroundBrush);
9050 dc.DrawPolygon(5, pts);
9051 }
9052
9053 if (m_bitmap.IsOk())
9054 {
9055 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9056 int y = clientArea.y + m_verticalPadding;
9057 dc.DrawBitmap(m_bitmap, x, y, true);
9058
9059 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9060 {
9061 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9062 wxCheckSetPen(dc, *wxBLACK_PEN);
9063 dc.SetLogicalFunction(wxINVERT);
9064 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9065 dc.SetLogicalFunction(wxCOPY);
9066 }
9067 }
9068 else
9069 {
9070 wxString label(m_label);
9071 if (label.IsEmpty())
9072 label = wxT("??");
9073 int w, h, maxDescent;
9074 dc.SetFont(m_font);
9075 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9076 dc.SetTextForeground(textColour);
9077
9078 int x = clientArea.x + (clientArea.width - w)/2;
9079 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9080 dc.DrawText(m_label, x, y);
9081 }
9082 }
9083
9084 return true;
9085}
9086
9087bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9088{
9089 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9090 return false; // USe default composite layout
9091
9092 wxSize size = GetSize(obj, dc, context, style);
9093 obj->SetCachedSize(size);
9094 obj->SetMinSize(size);
9095 obj->SetMaxSize(size);
9096 return true;
9097}
9098
9099bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
9100{
9101 if (IsTopLevel(obj))
9102 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position);
9103 else
9104 {
9105 wxSize sz = GetSize(obj, dc, context, 0);
9106 if (partialExtents)
9107 {
9108 int lastSize;
9109 if (partialExtents->GetCount() > 0)
9110 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9111 else
9112 lastSize = 0;
9113 partialExtents->Add(lastSize + sz.x);
9114 }
9115 size = sz;
9116 return true;
9117 }
9118}
9119
9120wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9121{
9122 int borderSize = 1;
9123 int w = 0, h = 0, maxDescent = 0;
9124
9125 wxSize sz;
9126 if (m_bitmap.IsOk())
9127 {
9128 w = m_bitmap.GetWidth();
9129 h = m_bitmap.GetHeight();
9130
9131 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9132 }
9133 else
9134 {
9135 wxString label(m_label);
9136 if (label.IsEmpty())
9137 label = wxT("??");
9138 dc.SetFont(m_font);
9139 dc.GetTextExtent(label, & w, &h, & maxDescent);
9140
9141 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9142 }
9143
9144 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9145 {
9146 sz.x += borderSize*2;
9147 sz.y += borderSize*2;
9148 }
9149
9150 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9151 {
9152 // Add space for the arrow
9153 sz.x += (sz.y/2 - m_horizontalPadding);
9154 }
9155
9156 return sz;
9157}
9158
603f702b
JS
9159IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9160
9161wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9162 wxRichTextBox(parent)
bec80f4f 9163{
603f702b
JS
9164}
9165
9166/// Draw the item
8db2e3ef 9167bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9168{
8db2e3ef 9169 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
9170}
9171
9172/// Copy
9173void wxRichTextCell::Copy(const wxRichTextCell& obj)
9174{
9175 wxRichTextBox::Copy(obj);
9176}
9177
9178// Edit properties via a GUI
9179bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9180{
9181 // We need to gather common attributes for all selected cells.
9182
9183 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9184 bool multipleCells = false;
9185 wxRichTextAttr attr;
9186
9187 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9188 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 9189 {
603f702b
JS
9190 wxRichTextAttr clashingAttr, absentAttr;
9191 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9192 size_t i;
9193 int selectedCellCount = 0;
9194 for (i = 0; i < sel.GetCount(); i++)
9195 {
9196 const wxRichTextRange& range = sel[i];
9197 wxRichTextCell* cell = table->GetCell(range.GetStart());
9198 if (cell)
9199 {
9200 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 9201
603f702b
JS
9202 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9203
9204 selectedCellCount ++;
9205 }
9206 }
9207 multipleCells = selectedCellCount > 1;
5ad9ae3a 9208 }
603f702b
JS
9209 else
9210 {
9211 attr = GetAttributes();
9212 }
9213
9214 wxString caption;
9215 if (multipleCells)
9216 caption = _("Multiple Cell Properties");
9217 else
9218 caption = _("Cell Properties");
9219
9220 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9221 cellDlg.SetAttributes(attr);
9222
80a46597 9223 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
603f702b
JS
9224 if (sizePage)
9225 {
9226 // We don't want position and floating controls for a cell.
9227 sizePage->ShowPositionControls(false);
9228 sizePage->ShowFloatingControls(false);
9229 }
9230
9231 if (cellDlg.ShowModal() == wxID_OK)
9232 {
9233 if (multipleCells)
9234 {
9235 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9236 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9237 // since it may represent clashing attributes across multiple objects.
9238 table->SetCellStyle(sel, attr);
9239 }
9240 else
9241 // For a single object, indeterminate attributes set by the user should be reflected in the
9242 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9243 // the style directly instead of applying (which ignores indeterminate attributes,
9244 // leaving them as they were).
9245 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9246 return true;
9247 }
9248 else
9249 return false;
9250}
9251
9252WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9253
9254IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9255
9256wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9257{
9258 m_rowCount = 0;
9259 m_colCount = 0;
9260}
5ad9ae3a 9261
603f702b 9262// Draws the object.
8db2e3ef 9263bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9264{
8db2e3ef 9265 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
9266}
9267
603f702b
JS
9268WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9269WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9270
9271// Lays the object out. rect is the space available for layout. Often it will
9272// be the specified overall space for this object, if trying to constrain
9273// layout to a particular size, or it could be the total space available in the
9274// parent. rect is the overall size, so we must subtract margins and padding.
9275// to get the actual available space.
8db2e3ef 9276bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 9277{
603f702b
JS
9278 SetPosition(rect.GetPosition());
9279
9280 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9281 // minimum size if within alloted size, then divide up remaining size
9282 // between rows/cols.
9283
9284 double scale = 1.0;
9285 wxRichTextBuffer* buffer = GetBuffer();
9286 if (buffer) scale = buffer->GetScale();
9287
8db2e3ef 9288 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
9289 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9290
8db2e3ef
JS
9291 wxRichTextAttr attr(GetAttributes());
9292 context.ApplyVirtualAttributes(attr, this);
9293
603f702b
JS
9294 // If we have no fixed table size, and assuming we're not pushed for
9295 // space, then we don't have to try to stretch the table to fit the contents.
9296 bool stretchToFitTableWidth = false;
9297
9298 int tableWidth = rect.width;
8db2e3ef 9299 if (attr.GetTextBoxAttr().GetWidth().IsValid())
603f702b 9300 {
8db2e3ef 9301 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
9302
9303 // Fixed table width, so we do want to stretch columns out if necessary.
9304 stretchToFitTableWidth = true;
9305
9306 // Shouldn't be able to exceed the size passed to this function
9307 tableWidth = wxMin(rect.width, tableWidth);
9308 }
9309
9310 // Get internal padding
36307fdf 9311 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
9312 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9313 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9314 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9315 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
9316
9317 // Assume that left and top padding are also used for inter-cell padding.
9318 int paddingX = paddingLeft;
9319 int paddingY = paddingTop;
9320
9321 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 9322 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
9323
9324 // Internal table width - the area for content
9325 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9326
9327 int rowCount = m_cells.GetCount();
9328 if (m_colCount == 0 || rowCount == 0)
9329 {
9330 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9331 SetCachedSize(overallRect.GetSize());
9332
9333 // Zero content size
9334 SetMinSize(overallRect.GetSize());
9335 SetMaxSize(GetMinSize());
9336 return true;
9337 }
9338
9339 // The final calculated widths
bb7bbd12
JS
9340 wxArrayInt colWidths;
9341 colWidths.Add(0, m_colCount);
603f702b 9342
bb7bbd12
JS
9343 wxArrayInt absoluteColWidths;
9344 absoluteColWidths.Add(0, m_colCount);
7c9fdebe 9345
bb7bbd12
JS
9346 wxArrayInt percentageColWidths;
9347 percentageColWidths.Add(0, m_colCount);
603f702b
JS
9348 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9349 // These are only relevant when the first column contains spanning information.
9350 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
9351 wxArrayInt maxColWidths;
9352 maxColWidths.Add(0, m_colCount);
9353 wxArrayInt minColWidths;
9354 minColWidths.Add(0, m_colCount);
603f702b
JS
9355
9356 wxSize tableSize(tableWidth, 0);
9357
9358 int i, j, k;
9359
9360 for (i = 0; i < m_colCount; i++)
9361 {
9362 absoluteColWidths[i] = 0;
9363 // absoluteColWidthsSpanning[i] = 0;
9364 percentageColWidths[i] = -1;
9365 // percentageColWidthsSpanning[i] = -1;
9366 colWidths[i] = 0;
9367 maxColWidths[i] = 0;
9368 minColWidths[i] = 0;
9369 // columnSpans[i] = 1;
9370 }
9371
9372 // (0) Determine which cells are visible according to spans
9373 // 1 2 3 4 5
9374 // __________________
9375 // | | | | | 1
9376 // |------| |----|
9377 // |------| | | 2
9378 // |------| | | 3
9379 // |------------------|
9380 // |__________________| 4
9381
9382 // To calculate cell visibility:
9383 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9384 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9385 // that cell, hide the cell.
9386
9387 // We can also use this array to match the size of spanning cells to the grid. Or just do
9388 // this when we iterate through all cells.
9389
9390 // 0.1: add spanning cells to an array
9391 wxRichTextRectArray rectArray;
9392 for (j = 0; j < m_rowCount; j++)
9393 {
9394 for (i = 0; i < m_colCount; i++)
9395 {
9396 wxRichTextBox* cell = GetCell(j, i);
9397 int colSpan = 1, rowSpan = 1;
9398 if (cell->GetProperties().HasProperty(wxT("colspan")))
9399 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9400 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9401 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9402 if (colSpan > 1 || rowSpan > 1)
9403 {
9404 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9405 }
9406 }
9407 }
9408 // 0.2: find which cells are subsumed by a spanning cell
9409 for (j = 0; j < m_rowCount; j++)
9410 {
9411 for (i = 0; i < m_colCount; i++)
9412 {
9413 wxRichTextBox* cell = GetCell(j, i);
9414 if (rectArray.GetCount() == 0)
9415 {
9416 cell->Show(true);
9417 }
9418 else
9419 {
9420 int colSpan = 1, rowSpan = 1;
9421 if (cell->GetProperties().HasProperty(wxT("colspan")))
9422 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9423 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9424 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9425 if (colSpan > 1 || rowSpan > 1)
9426 {
9427 // Assume all spanning cells are shown
9428 cell->Show(true);
9429 }
9430 else
9431 {
9432 bool shown = true;
9433 for (k = 0; k < (int) rectArray.GetCount(); k++)
9434 {
9435 if (rectArray[k].Contains(wxPoint(i, j)))
9436 {
9437 shown = false;
9438 break;
9439 }
9440 }
9441 cell->Show(shown);
9442 }
9443 }
9444 }
9445 }
9446
9447 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9448 // overlap with a spanned cell starting at a previous column position.
9449 // This means we need to keep an array of rects so we can check. However
9450 // it does also mean that some spans simply may not be taken into account
9451 // where there are different spans happening on different rows. In these cases,
9452 // they will simply be as wide as their constituent columns.
9453
9454 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9455 // the absolute or percentage width of each column.
9456
9457 for (j = 0; j < m_rowCount; j++)
9458 {
9459 // First get the overall margins so we can calculate percentage widths based on
9460 // the available content space for all cells on the row
9461
9462 int overallRowContentMargin = 0;
9463 int visibleCellCount = 0;
9464
9465 for (i = 0; i < m_colCount; i++)
9466 {
9467 wxRichTextBox* cell = GetCell(j, i);
9468 if (cell->IsShown())
9469 {
9470 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9471 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9472
9473 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9474 visibleCellCount ++;
9475 }
9476 }
9477
9478 // Add in inter-cell padding
9479 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9480
9481 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9482 wxSize rowTableSize(rowContentWidth, 0);
9483 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9484
9485 for (i = 0; i < m_colCount; i++)
9486 {
9487 wxRichTextBox* cell = GetCell(j, i);
9488 if (cell->IsShown())
9489 {
9490 int colSpan = 1;
9491 if (cell->GetProperties().HasProperty(wxT("colspan")))
9492 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9493
9494 // Lay out cell to find min/max widths
9495 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9496 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
9497
9498 if (colSpan == 1)
9499 {
9500 int absoluteCellWidth = -1;
9501 int percentageCellWidth = -1;
9502
9503 // I think we need to calculate percentages from the internal table size,
9504 // minus the padding between cells which we'll need to calculate from the
9505 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9506 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9507 // so if we want to conform to that we'll need to add in the overall cell margins.
9508 // However, this will make it difficult to specify percentages that add up to
9509 // 100% and still fit within the table width.
9510 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9511 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9512 // If we're using internal content size for the width, we would calculate the
9513 // the overall cell width for n cells as:
9514 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9515 // + thisOverallCellMargin
9516 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9517 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9518
9519 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9520 {
9521 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9522 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9523 {
9524 percentageCellWidth = w;
9525 }
9526 else
9527 {
9528 absoluteCellWidth = w;
9529 }
9530 // Override absolute width with minimum width if necessary
9531 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9532 absoluteCellWidth = cell->GetMinSize().x;
9533 }
9534
9535 if (absoluteCellWidth != -1)
9536 {
9537 if (absoluteCellWidth > absoluteColWidths[i])
9538 absoluteColWidths[i] = absoluteCellWidth;
9539 }
9540
9541 if (percentageCellWidth != -1)
9542 {
9543 if (percentageCellWidth > percentageColWidths[i])
9544 percentageColWidths[i] = percentageCellWidth;
9545 }
9546
9547 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9548 minColWidths[i] = cell->GetMinSize().x;
9549 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9550 maxColWidths[i] = cell->GetMaxSize().x;
9551 }
9552 }
9553 }
9554 }
9555
9556 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9557 // TODO: simply merge this into (1).
9558 for (i = 0; i < m_colCount; i++)
9559 {
9560 if (absoluteColWidths[i] > 0)
9561 {
9562 colWidths[i] = absoluteColWidths[i];
9563 }
9564 else if (percentageColWidths[i] > 0)
9565 {
9566 colWidths[i] = percentageColWidths[i];
9567
9568 // This is rubbish - we calculated the absolute widths from percentages, so
9569 // we can't do it again here.
9570 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9571 }
9572 }
9573
9574 // (3) Process absolute or proportional widths of spanning columns,
9575 // now that we know what our fixed column widths are going to be.
9576 // Spanned cells will try to adjust columns so the span will fit.
9577 // Even existing fixed column widths can be expanded if necessary.
9578 // Actually, currently fixed columns widths aren't adjusted; instead,
9579 // the algorithm favours earlier rows and adjusts unspecified column widths
9580 // the first time only. After that, we can't know whether the column has been
9581 // specified explicitly or not. (We could make a note if necessary.)
9582 for (j = 0; j < m_rowCount; j++)
9583 {
9584 // First get the overall margins so we can calculate percentage widths based on
9585 // the available content space for all cells on the row
9586
9587 int overallRowContentMargin = 0;
9588 int visibleCellCount = 0;
9589
9590 for (i = 0; i < m_colCount; i++)
9591 {
9592 wxRichTextBox* cell = GetCell(j, i);
9593 if (cell->IsShown())
9594 {
9595 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9596 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9597
9598 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9599 visibleCellCount ++;
9600 }
9601 }
9602
9603 // Add in inter-cell padding
9604 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9605
9606 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9607 wxSize rowTableSize(rowContentWidth, 0);
9608 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9609
9610 for (i = 0; i < m_colCount; i++)
9611 {
9612 wxRichTextBox* cell = GetCell(j, i);
9613 if (cell->IsShown())
9614 {
9615 int colSpan = 1;
9616 if (cell->GetProperties().HasProperty(wxT("colspan")))
9617 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9618
9619 if (colSpan > 1)
9620 {
9621 int spans = wxMin(colSpan, m_colCount - i);
9622 int cellWidth = 0;
9623 if (spans > 0)
9624 {
9625 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9626 {
9627 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9628 // Override absolute width with minimum width if necessary
9629 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9630 cellWidth = cell->GetMinSize().x;
9631 }
9632 else
9633 {
9634 // Do we want to do this? It's the only chance we get to
9635 // use the cell's min/max sizes, so we need to work out
9636 // how we're going to balance the unspecified spanning cell
9637 // width with the possibility more-constrained constituent cell widths.
9638 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9639 // don't want to constraint all the spanned columns to fit into this cell.
9640 // OK, let's say that if any of the constituent columns don't fit,
9641 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9642 // cells to the columns later.
9643 cellWidth = cell->GetMinSize().x;
9644 if (cell->GetMaxSize().x > cellWidth)
9645 cellWidth = cell->GetMaxSize().x;
9646 }
9647
9648 // Subtract the padding between cells
9649 int spanningWidth = cellWidth;
9650 spanningWidth -= paddingX * (spans-1);
9651
9652 if (spanningWidth > 0)
9653 {
9654 // Now share the spanning width between columns within that span
9655 // TODO: take into account min widths of columns within the span
9656 int spanningWidthLeft = spanningWidth;
9657 int stretchColCount = 0;
9658 for (k = i; k < (i+spans); k++)
9659 {
9660 if (colWidths[k] > 0) // absolute or proportional width has been specified
9661 spanningWidthLeft -= colWidths[k];
9662 else
9663 stretchColCount ++;
9664 }
9665 // Now divide what's left between the remaining columns
9666 int colShare = 0;
9667 if (stretchColCount > 0)
9668 colShare = spanningWidthLeft / stretchColCount;
9669 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9670
9671 // If fixed-width columns are currently too big, then we'll later
9672 // stretch the spanned cell to fit.
9673
9674 if (spanningWidthLeft > 0)
9675 {
9676 for (k = i; k < (i+spans); k++)
9677 {
9678 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9679 {
9680 int newWidth = colShare;
9681 if (k == (i+spans-1))
9682 newWidth += colShareRemainder; // ensure all pixels are filled
9683 colWidths[k] = newWidth;
9684 }
9685 }
9686 }
9687 }
9688 }
9689 }
9690 }
9691 }
9692 }
9693
9694 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9695 // TODO: take into account min widths of columns within the span
9696 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9697 int widthLeft = tableWidthMinusPadding;
9698 int stretchColCount = 0;
9699 for (i = 0; i < m_colCount; i++)
9700 {
9701 // TODO: we need to take into account min widths.
9702 // Subtract min width from width left, then
9703 // add the colShare to the min width
9704 if (colWidths[i] > 0) // absolute or proportional width has been specified
9705 widthLeft -= colWidths[i];
9706 else
9707 {
9708 if (minColWidths[i] > 0)
9709 widthLeft -= minColWidths[i];
9710
9711 stretchColCount ++;
9712 }
9713 }
9714
9715 // Now divide what's left between the remaining columns
9716 int colShare = 0;
9717 if (stretchColCount > 0)
9718 colShare = widthLeft / stretchColCount;
9719 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9720
9721 // Check we don't have enough space, in which case shrink all columns, overriding
9722 // any absolute/proportional widths
9723 // TODO: actually we would like to divide up the shrinkage according to size.
9724 // How do we calculate the proportions that will achieve this?
9725 // Could first choose an arbitrary value for stretching cells, and then calculate
9726 // factors to multiply each width by.
9727 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9728 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9729 {
9730 colShare = tableWidthMinusPadding / m_colCount;
9731 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9732 for (i = 0; i < m_colCount; i++)
9733 {
9734 colWidths[i] = 0;
9735 minColWidths[i] = 0;
9736 }
9737 }
9738
9739 // We have to adjust the columns if either we need to shrink the
9740 // table to fit the parent/table width, or we explicitly set the
9741 // table width and need to stretch out the table.
9742 if (widthLeft < 0 || stretchToFitTableWidth)
9743 {
9744 for (i = 0; i < m_colCount; i++)
9745 {
9746 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9747 {
9748 if (minColWidths[i] > 0)
9749 colWidths[i] = minColWidths[i] + colShare;
9750 else
9751 colWidths[i] = colShare;
9752 if (i == (m_colCount-1))
9753 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9754 }
9755 }
9756 }
9757
9758 // TODO: if spanned cells have no specified or max width, make them the
9759 // as big as the columns they span. Do this for all spanned cells in all
9760 // rows, of course. Size any spanned cells left over at the end - even if they
9761 // have width > 0, make sure they're limited to the appropriate column edge.
9762
9763
9764/*
9765 Sort out confusion between content width
9766 and overall width later. For now, assume we specify overall width.
9767
9768 So, now we've laid out the table to fit into the given space
9769 and have used specified widths and minimum widths.
9770
9771 Now we need to consider how we will try to take maximum width into account.
9772
9773*/
9774
9775 // (??) TODO: take max width into account
9776
9777 // (6) Lay out all cells again with the current values
9778
9779 int maxRight = 0;
9780 int y = availableSpace.y;
9781 for (j = 0; j < m_rowCount; j++)
9782 {
9783 int x = availableSpace.x; // TODO: take into account centering etc.
9784 int maxCellHeight = 0;
9785 int maxSpecifiedCellHeight = 0;
9786
bb7bbd12
JS
9787 wxArrayInt actualWidths;
9788 actualWidths.Add(0, m_colCount);
603f702b
JS
9789
9790 wxTextAttrDimensionConverter converter(dc, scale);
9791 for (i = 0; i < m_colCount; i++)
9792 {
9793 wxRichTextCell* cell = GetCell(j, i);
9794 if (cell->IsShown())
9795 {
603f702b
JS
9796 // Get max specified cell height
9797 // Don't handle percentages for height
9798 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9799 {
9800 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9801 if (h > maxSpecifiedCellHeight)
9802 maxSpecifiedCellHeight = h;
9803 }
9804
9805 if (colWidths[i] > 0) // absolute or proportional width has been specified
9806 {
9807 int colSpan = 1;
9808 if (cell->GetProperties().HasProperty(wxT("colspan")))
9809 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9810
9811 wxRect availableCellSpace;
9812
9813 // TODO: take into acount spans
9814 if (colSpan > 1)
9815 {
9816 // Calculate the size of this spanning cell from its constituent columns
9817 int xx = x;
9818 int spans = wxMin(colSpan, m_colCount - i);
9819 for (k = i; k < spans; k++)
9820 {
9821 if (k != i)
9822 xx += paddingX;
9823 xx += colWidths[k];
9824 }
9825 availableCellSpace = wxRect(x, y, xx, -1);
9826 }
9827 else
9828 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9829
9830 // Store actual width so we can force cell to be the appropriate width on the final loop
9831 actualWidths[i] = availableCellSpace.GetWidth();
9832
9833 // Lay out cell
9834 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9835 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9836
9837 // TODO: use GetCachedSize().x to compute 'natural' size
9838
9839 x += (availableCellSpace.GetWidth() + paddingX);
9840 if (cell->GetCachedSize().y > maxCellHeight)
9841 maxCellHeight = cell->GetCachedSize().y;
9842 }
9843 }
9844 }
9845
9846 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9847
9848 for (i = 0; i < m_colCount; i++)
9849 {
9850 wxRichTextCell* cell = GetCell(j, i);
9851 if (cell->IsShown())
9852 {
9853 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9854 // Lay out cell with new height
9855 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9856 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9857
9858 // Make sure the cell size really is the appropriate size,
9859 // not the calculated box size
9860 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9861
9862 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9863 }
9864 }
9865
9866 y += maxCellHeight;
9867 if (j < (m_rowCount-1))
9868 y += paddingY;
9869 }
9870
9871 // We need to add back the margins etc.
9872 {
9873 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9874 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 9875 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
9876 SetCachedSize(marginRect.GetSize());
9877 }
9878
9879 // TODO: calculate max size
9880 {
9881 SetMaxSize(GetCachedSize());
9882 }
9883
9884 // TODO: calculate min size
9885 {
9886 SetMinSize(GetCachedSize());
9887 }
9888
9889 // TODO: currently we use either a fixed table width or the parent's size.
9890 // We also want to be able to calculate the table width from its content,
9891 // whether using fixed column widths or cell content min/max width.
9892 // Probably need a boolean flag to say whether we need to stretch cells
9893 // to fit the table width, or to simply use min/max cell widths. The
9894 // trouble with this is that if cell widths are not specified, they
9895 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9896 // Anyway, ignoring that problem, we probably need to factor layout into a function
9897 // that can can calculate the maximum unconstrained layout in case table size is
9898 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9899 // constrain Layout(), or the previously-calculated max size to constraint layout.
9900
9901 return true;
9902}
9903
9904// Finds the absolute position and row height for the given character position
8db2e3ef 9905bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
9906{
9907 wxRichTextCell* child = GetCell(index+1);
9908 if (child)
9909 {
9910 // Find the position at the start of the child cell, since the table doesn't
9911 // have any caret position of its own.
8db2e3ef 9912 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
9913 }
9914 else
9915 return false;
9916}
9917
9918// Get the cell at the given character position (in the range of the table).
9919wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9920{
9921 int row = 0, col = 0;
9922 if (GetCellRowColumnPosition(pos, row, col))
9923 {
9924 return GetCell(row, col);
9925 }
9926 else
9927 return NULL;
9928}
9929
9930// Get the row/column for a given character position
9931bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9932{
9933 if (m_colCount == 0 || m_rowCount == 0)
9934 return false;
9935
9936 row = (int) (pos / m_colCount);
9937 col = pos - (row * m_colCount);
9938
9939 wxASSERT(row < m_rowCount && col < m_colCount);
9940
9941 if (row < m_rowCount && col < m_colCount)
9942 return true;
9943 else
9944 return false;
9945}
9946
9947// Calculate range, taking row/cell ordering into account instead of relying
9948// on list ordering.
9949void wxRichTextTable::CalculateRange(long start, long& end)
9950{
9951 long current = start;
9952 long lastEnd = current;
9953
9954 if (IsTopLevel())
9955 {
9956 current = 0;
9957 lastEnd = 0;
9958 }
9959
9960 int i, j;
9961 for (i = 0; i < m_rowCount; i++)
9962 {
9963 for (j = 0; j < m_colCount; j++)
9964 {
9965 wxRichTextCell* child = GetCell(i, j);
9966 if (child)
9967 {
9968 long childEnd = 0;
9969
9970 child->CalculateRange(current, childEnd);
9971
9972 lastEnd = childEnd;
9973 current = childEnd + 1;
9974 }
9975 }
9976 }
9977
9978 // A top-level object always has a range of size 1,
9979 // because its children don't count at this level.
9980 end = start;
9981 m_range.SetRange(start, start);
9982
9983 // An object with no children has zero length
9984 if (m_children.GetCount() == 0)
9985 lastEnd --;
9986 m_ownRange.SetRange(0, lastEnd);
9987}
9988
9989// Gets the range size.
8db2e3ef 9990bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b 9991{
8db2e3ef 9992 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
603f702b
JS
9993}
9994
9995// Deletes content in the given range.
9996bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
9997{
9998 // TODO: implement deletion of cells
9999 return true;
10000}
10001
10002// Gets any text in this object for the given range.
10003wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10004{
10005 return wxRichTextBox::GetTextForRange(range);
10006}
10007
10008// Copies this object.
10009void wxRichTextTable::Copy(const wxRichTextTable& obj)
10010{
10011 wxRichTextBox::Copy(obj);
10012
10013 ClearTable();
10014
10015 m_rowCount = obj.m_rowCount;
10016 m_colCount = obj.m_colCount;
10017
10018 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10019
10020 int i, j;
10021 for (i = 0; i < m_rowCount; i++)
10022 {
10023 wxRichTextObjectPtrArray& colArray = m_cells[i];
10024 for (j = 0; j < m_colCount; j++)
10025 {
10026 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10027 AppendChild(cell);
10028
10029 colArray.Add(cell);
10030 }
10031 }
10032}
10033
10034void wxRichTextTable::ClearTable()
10035{
10036 m_cells.Clear();
10037 DeleteChildren();
10038}
10039
10040bool wxRichTextTable::CreateTable(int rows, int cols)
10041{
10042 ClearTable();
10043
10044 m_rowCount = rows;
10045 m_colCount = cols;
10046
10047 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10048
10049 int i, j;
10050 for (i = 0; i < rows; i++)
10051 {
10052 wxRichTextObjectPtrArray& colArray = m_cells[i];
10053 for (j = 0; j < cols; j++)
10054 {
10055 wxRichTextCell* cell = new wxRichTextCell;
10056 AppendChild(cell);
10057 cell->AddParagraph(wxEmptyString);
10058
10059 colArray.Add(cell);
10060 }
10061 }
10062
10063 return true;
10064}
10065
10066wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10067{
10068 wxASSERT(row < m_rowCount);
10069 wxASSERT(col < m_colCount);
10070
10071 if (row < m_rowCount && col < m_colCount)
10072 {
10073 wxRichTextObjectPtrArray& colArray = m_cells[row];
10074 wxRichTextObject* obj = colArray[col];
10075 return wxDynamicCast(obj, wxRichTextCell);
10076 }
10077 else
d67faa04 10078 return NULL;
603f702b
JS
10079}
10080
10081// Returns a selection object specifying the selections between start and end character positions.
10082// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10083wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10084{
10085 wxRichTextSelection selection;
10086 selection.SetContainer((wxRichTextTable*) this);
10087
10088 if (start > end)
10089 {
10090 long tmp = end;
10091 end = start;
10092 start = tmp;
10093 }
10094
10095 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10096
10097 if (end >= (m_colCount * m_rowCount))
10098 return selection;
10099
10100 // We need to find the rectangle of cells that is described by the rectangle
10101 // with start, end as the diagonal. Make sure we don't add cells that are
10102 // not currenty visible because they are overlapped by spanning cells.
10103/*
10104 --------------------------
10105 | 0 | 1 | 2 | 3 | 4 |
10106 --------------------------
10107 | 5 | 6 | 7 | 8 | 9 |
10108 --------------------------
10109 | 10 | 11 | 12 | 13 | 14 |
10110 --------------------------
10111 | 15 | 16 | 17 | 18 | 19 |
10112 --------------------------
10113
10114 Let's say we select 6 -> 18.
10115
10116 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10117 which is left and which is right.
10118
10119 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10120
10121 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10122 and (b) shown.
10123
10124
10125*/
10126
10127 int leftCol = start - m_colCount * int(start/m_colCount);
10128 int rightCol = end - m_colCount * int(end/m_colCount);
10129
10130 int topRow = int(start/m_colCount);
10131 int bottomRow = int(end/m_colCount);
10132
10133 if (leftCol > rightCol)
10134 {
10135 int tmp = rightCol;
10136 rightCol = leftCol;
10137 leftCol = tmp;
10138 }
10139
10140 if (topRow > bottomRow)
10141 {
10142 int tmp = bottomRow;
10143 bottomRow = topRow;
10144 topRow = tmp;
10145 }
10146
10147 int i, j;
10148 for (i = topRow; i <= bottomRow; i++)
10149 {
10150 for (j = leftCol; j <= rightCol; j++)
10151 {
10152 wxRichTextCell* cell = GetCell(i, j);
10153 if (cell && cell->IsShown())
10154 selection.Add(cell->GetRange());
10155 }
10156 }
10157
10158 return selection;
10159}
10160
10161// Sets the attributes for the cells specified by the selection.
10162bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10163{
10164 if (selection.GetContainer() != this)
10165 return false;
10166
10167 wxRichTextBuffer* buffer = GetBuffer();
10168 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10169 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10170
10171 if (withUndo)
10172 buffer->BeginBatchUndo(_("Set Cell Style"));
10173
10174 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10175 while (node)
10176 {
10177 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10178 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10179 SetStyle(cell, style, flags);
10180 node = node->GetNext();
10181 }
10182
10183 // Do action, or delay it until end of batch.
10184 if (withUndo)
10185 buffer->EndBatchUndo();
10186
10187 return true;
10188}
10189
10190bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10191{
10192 wxASSERT((startRow + noRows) < m_rowCount);
10193 if ((startRow + noRows) >= m_rowCount)
10194 return false;
10195
10196 int i, j;
10197 for (i = startRow; i < (startRow+noRows); i++)
10198 {
10199 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10200 for (j = 0; j < (int) colArray.GetCount(); j++)
10201 {
10202 wxRichTextObject* cell = colArray[j];
10203 RemoveChild(cell, true);
10204 }
10205
10206 // Keep deleting at the same position, since we move all
10207 // the others up
10208 m_cells.RemoveAt(startRow);
10209 }
10210
10211 m_rowCount = m_rowCount - noRows;
10212
10213 return true;
10214}
10215
10216bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10217{
10218 wxASSERT((startCol + noCols) < m_colCount);
10219 if ((startCol + noCols) >= m_colCount)
10220 return false;
10221
10222 bool deleteRows = (noCols == m_colCount);
10223
10224 int i, j;
10225 for (i = 0; i < m_rowCount; i++)
10226 {
10227 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
10228 for (j = startCol; j < (startCol+noCols); j++)
10229 {
10230 wxRichTextObject* cell = colArray[j];
10231 RemoveChild(cell, true);
10232 }
10233
10234 if (deleteRows)
10235 m_cells.RemoveAt(0);
10236 }
10237
10238 if (deleteRows)
10239 m_rowCount = 0;
10240 m_colCount = m_colCount - noCols;
10241
10242 return true;
10243}
10244
10245bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10246{
10247 wxASSERT(startRow <= m_rowCount);
10248 if (startRow > m_rowCount)
10249 return false;
10250
10251 int i, j;
10252 for (i = 0; i < noRows; i++)
10253 {
10254 int idx;
10255 if (startRow == m_rowCount)
10256 {
10257 m_cells.Add(wxRichTextObjectPtrArray());
10258 idx = m_cells.GetCount() - 1;
10259 }
10260 else
10261 {
10262 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10263 idx = startRow+i;
10264 }
10265
10266 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10267 for (j = 0; j < m_colCount; j++)
10268 {
10269 wxRichTextCell* cell = new wxRichTextCell;
10270 cell->GetAttributes() = attr;
10271
10272 AppendChild(cell);
10273 colArray.Add(cell);
10274 }
10275 }
10276
10277 m_rowCount = m_rowCount + noRows;
10278 return true;
10279}
10280
10281bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10282{
10283 wxASSERT(startCol <= m_colCount);
10284 if (startCol > m_colCount)
10285 return false;
10286
10287 int i, j;
10288 for (i = 0; i < m_rowCount; i++)
10289 {
10290 wxRichTextObjectPtrArray& colArray = m_cells[i];
10291 for (j = 0; j < noCols; j++)
10292 {
10293 wxRichTextCell* cell = new wxRichTextCell;
10294 cell->GetAttributes() = attr;
10295
10296 AppendChild(cell);
10297
10298 if (startCol == m_colCount)
10299 colArray.Add(cell);
10300 else
10301 colArray.Insert(cell, startCol+j);
10302 }
10303 }
10304
10305 m_colCount = m_colCount + noCols;
10306
10307 return true;
5ad9ae3a
JS
10308}
10309
603f702b
JS
10310// Edit properties via a GUI
10311bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 10312{
603f702b
JS
10313 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10314 boxDlg.SetAttributes(GetAttributes());
10315
10316 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 10317 {
603f702b
JS
10318 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10319 return true;
5ad9ae3a
JS
10320 }
10321 else
10322 return false;
bec80f4f
JS
10323}
10324
5d7836c4
JS
10325/*
10326 * Module to initialise and clean up handlers
10327 */
10328
10329class wxRichTextModule: public wxModule
10330{
10331DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10332public:
10333 wxRichTextModule() {}
cfa3b256
JS
10334 bool OnInit()
10335 {
d2d0adc7 10336 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
10337 wxRichTextBuffer::InitStandardHandlers();
10338 wxRichTextParagraph::InitDefaultTabs();
1aca9fcd
JS
10339
10340 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10341 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10342 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10343 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10344 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10345 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10346 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10347 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10348 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10349
cfa3b256 10350 return true;
47b378bd 10351 }
cfa3b256
JS
10352 void OnExit()
10353 {
10354 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 10355 wxRichTextBuffer::CleanUpDrawingHandlers();
7c9fdebe 10356 wxRichTextBuffer::CleanUpFieldTypes();
1aca9fcd 10357 wxRichTextXMLHandler::ClearNodeToClassMap();
cfa3b256
JS
10358 wxRichTextDecimalToRoman(-1);
10359 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 10360 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 10361 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 10362 }
5d7836c4
JS
10363};
10364
10365IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10366
10367
f1d6804f
RD
10368// If the richtext lib is dynamically loaded after the app has already started
10369// (such as from wxPython) then the built-in module system will not init this
10370// module. Provide this function to do it manually.
10371void wxRichTextModuleInit()
10372{
10373 wxModule* module = new wxRichTextModule;
10374 module->Init();
10375 wxModule::RegisterModule(module);
10376}
10377
10378
5d7836c4
JS
10379/*!
10380 * Commands for undo/redo
10381 *
10382 */
10383
10384wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 10385 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 10386{
603f702b 10387 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
10388}
10389
7fe8059f 10390wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
10391{
10392}
10393
10394wxRichTextCommand::~wxRichTextCommand()
10395{
10396 ClearActions();
10397}
10398
10399void wxRichTextCommand::AddAction(wxRichTextAction* action)
10400{
10401 if (!m_actions.Member(action))
10402 m_actions.Append(action);
10403}
10404
10405bool wxRichTextCommand::Do()
10406{
09f14108 10407 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
10408 {
10409 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10410 action->Do();
10411 }
10412
10413 return true;
10414}
10415
10416bool wxRichTextCommand::Undo()
10417{
09f14108 10418 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
10419 {
10420 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10421 action->Undo();
10422 }
10423
10424 return true;
10425}
10426
10427void wxRichTextCommand::ClearActions()
10428{
10429 WX_CLEAR_LIST(wxList, m_actions);
10430}
10431
10432/*!
10433 * Individual action
10434 *
10435 */
10436
603f702b
JS
10437wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10438 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10439 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
10440{
10441 m_buffer = buffer;
603f702b
JS
10442 m_object = NULL;
10443 m_containerAddress.Create(buffer, container);
5d7836c4
JS
10444 m_ignoreThis = ignoreFirstTime;
10445 m_cmdId = id;
10446 m_position = -1;
10447 m_ctrl = ctrl;
10448 m_name = name;
10449 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10450 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10451 if (cmd)
10452 cmd->AddAction(this);
10453}
10454
10455wxRichTextAction::~wxRichTextAction()
10456{
603f702b
JS
10457 if (m_object)
10458 delete m_object;
10459}
10460
10461// Returns the container that this action refers to, using the container address and top-level buffer.
10462wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10463{
10464 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10465 return container;
5d7836c4
JS
10466}
10467
603f702b 10468
7051fa41
JS
10469void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10470{
10471 // Store a list of line start character and y positions so we can figure out which area
10472 // we need to refresh
10473
10474#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
10475 wxRichTextParagraphLayoutBox* container = GetContainer();
10476 wxASSERT(container != NULL);
10477 if (!container)
10478 return;
10479
7051fa41
JS
10480 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10481 // If we had several actions, which only invalidate and leave layout until the
10482 // paint handler is called, then this might not be true. So we may need to switch
10483 // optimisation on only when we're simply adding text and not simultaneously
10484 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10485 // first, but of course this means we'll be doing it twice.
603f702b 10486 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41 10487 {
4ba36292
JS
10488 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10489 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
7051fa41
JS
10490 int lastY = firstVisiblePt.y + clientSize.y;
10491
603f702b
JS
10492 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10493 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
10494 while (node)
10495 {
10496 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10497 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10498 while (node2)
10499 {
10500 wxRichTextLine* line = node2->GetData();
10501 wxPoint pt = line->GetAbsolutePosition();
10502 wxRichTextRange range = line->GetAbsoluteRange();
10503
10504 if (pt.y > lastY)
10505 {
10506 node2 = wxRichTextLineList::compatibility_iterator();
10507 node = wxRichTextObjectList::compatibility_iterator();
10508 }
10509 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10510 {
10511 optimizationLineCharPositions.Add(range.GetStart());
10512 optimizationLineYPositions.Add(pt.y);
10513 }
10514
10515 if (node2)
10516 node2 = node2->GetNext();
10517 }
10518
10519 if (node)
10520 node = node->GetNext();
10521 }
10522 }
10523#endif
10524}
10525
5d7836c4
JS
10526bool wxRichTextAction::Do()
10527{
10528 m_buffer->Modify(true);
10529
603f702b
JS
10530 wxRichTextParagraphLayoutBox* container = GetContainer();
10531 wxASSERT(container != NULL);
10532 if (!container)
10533 return false;
10534
5d7836c4
JS
10535 switch (m_cmdId)
10536 {
10537 case wxRICHTEXT_INSERT:
10538 {
ea160b2e
JS
10539 // Store a list of line start character and y positions so we can figure out which area
10540 // we need to refresh
10541 wxArrayInt optimizationLineCharPositions;
10542 wxArrayInt optimizationLineYPositions;
10543
10544#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 10545 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
10546#endif
10547
603f702b
JS
10548 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10549 container->UpdateRanges();
10550
10551 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10552 // Layout() would stop prematurely at the top level.
10553 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 10554
603f702b 10555 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
10556
10557 // Character position to caret position
10558 newCaretPosition --;
10559
10560 // Don't take into account the last newline
5d7836c4
JS
10561 if (m_newParagraphs.GetPartialParagraph())
10562 newCaretPosition --;
46ee0e5b 10563 else
7c081bd2 10564 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
10565 {
10566 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10567 if (p->GetRange().GetLength() == 1)
10568 newCaretPosition --;
10569 }
5d7836c4 10570
603f702b 10571 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 10572
7051fa41 10573 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 10574
5912d19e
JS
10575 wxRichTextEvent cmdEvent(
10576 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10577 m_ctrl ? m_ctrl->GetId() : -1);
10578 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10579 cmdEvent.SetRange(GetRange());
10580 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10581 cmdEvent.SetContainer(container);
3e541562 10582
5912d19e 10583 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
10584
10585 break;
10586 }
10587 case wxRICHTEXT_DELETE:
10588 {
7051fa41
JS
10589 wxArrayInt optimizationLineCharPositions;
10590 wxArrayInt optimizationLineYPositions;
10591
10592#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10593 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10594#endif
10595
603f702b
JS
10596 container->DeleteRange(GetRange());
10597 container->UpdateRanges();
10598 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10599 // Layout() would stop prematurely at the top level.
10600 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 10601
6ccbca24 10602 long caretPos = GetRange().GetStart()-1;
603f702b 10603 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
10604 caretPos --;
10605
7051fa41 10606 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 10607
5912d19e
JS
10608 wxRichTextEvent cmdEvent(
10609 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10610 m_ctrl ? m_ctrl->GetId() : -1);
10611 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10612 cmdEvent.SetRange(GetRange());
10613 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10614 cmdEvent.SetContainer(container);
3e541562 10615
5912d19e
JS
10616 m_buffer->SendEvent(cmdEvent);
10617
5d7836c4
JS
10618 break;
10619 }
10620 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10621 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10622 {
10623 ApplyParagraphs(GetNewParagraphs());
603f702b 10624
c4168888 10625 // Invalidate the whole buffer if there were floating objects
e12b91a3 10626 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10627 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10628 else
10629 {
10630 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10631 // Layout() would stop prematurely at the top level.
10632 container->InvalidateHierarchy(GetRange());
10633 }
603f702b
JS
10634
10635 UpdateAppearance(GetPosition());
10636
10637 wxRichTextEvent cmdEvent(
590a0f8b 10638 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
10639 m_ctrl ? m_ctrl->GetId() : -1);
10640 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10641 cmdEvent.SetRange(GetRange());
10642 cmdEvent.SetPosition(GetRange().GetStart());
10643 cmdEvent.SetContainer(container);
10644
10645 m_buffer->SendEvent(cmdEvent);
10646
10647 break;
10648 }
10649 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10650 {
10651 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10652 if (obj)
10653 {
10654 wxRichTextAttr oldAttr = obj->GetAttributes();
10655 obj->GetAttributes() = m_attributes;
10656 m_attributes = oldAttr;
10657 }
10658
10659 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10660 // Layout() would stop prematurely at the top level.
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 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10666
10667 UpdateAppearance(GetPosition());
10668
5912d19e
JS
10669 wxRichTextEvent cmdEvent(
10670 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10671 m_ctrl ? m_ctrl->GetId() : -1);
10672 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10673 cmdEvent.SetRange(GetRange());
10674 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10675 cmdEvent.SetContainer(container);
3e541562 10676
5912d19e
JS
10677 m_buffer->SendEvent(cmdEvent);
10678
603f702b
JS
10679 break;
10680 }
10681 case wxRICHTEXT_CHANGE_OBJECT:
10682 {
10683 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10684 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10685 if (obj && m_object)
10686 {
10687 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10688 if (node)
10689 {
10690 wxRichTextObject* obj = node->GetData();
10691 node->SetData(m_object);
10692 m_object = obj;
10693 }
10694 }
10695
10696 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10697 // Layout() would stop prematurely at the top level.
c4168888 10698 // Invalidate the whole buffer if there were floating objects
e12b91a3 10699 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10700 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10701 else
10702 container->InvalidateHierarchy(GetRange());
603f702b
JS
10703
10704 UpdateAppearance(GetPosition());
10705
10706 // TODO: send new kind of modification event
10707
5d7836c4
JS
10708 break;
10709 }
10710 default:
10711 break;
10712 }
10713
10714 return true;
10715}
10716
10717bool wxRichTextAction::Undo()
10718{
10719 m_buffer->Modify(true);
10720
603f702b
JS
10721 wxRichTextParagraphLayoutBox* container = GetContainer();
10722 wxASSERT(container != NULL);
10723 if (!container)
10724 return false;
10725
5d7836c4
JS
10726 switch (m_cmdId)
10727 {
10728 case wxRICHTEXT_INSERT:
10729 {
7051fa41
JS
10730 wxArrayInt optimizationLineCharPositions;
10731 wxArrayInt optimizationLineYPositions;
10732
10733#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10734 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10735#endif
10736
603f702b
JS
10737 container->DeleteRange(GetRange());
10738 container->UpdateRanges();
7c9fdebe 10739
603f702b
JS
10740 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10741 // Layout() would stop prematurely at the top level.
10742 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
10743
10744 long newCaretPosition = GetPosition() - 1;
3e541562 10745
7051fa41 10746 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10747
5912d19e
JS
10748 wxRichTextEvent cmdEvent(
10749 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10750 m_ctrl ? m_ctrl->GetId() : -1);
10751 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10752 cmdEvent.SetRange(GetRange());
10753 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10754 cmdEvent.SetContainer(container);
3e541562 10755
5912d19e
JS
10756 m_buffer->SendEvent(cmdEvent);
10757
5d7836c4
JS
10758 break;
10759 }
10760 case wxRICHTEXT_DELETE:
10761 {
7051fa41
JS
10762 wxArrayInt optimizationLineCharPositions;
10763 wxArrayInt optimizationLineYPositions;
10764
10765#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10766 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10767#endif
10768
603f702b
JS
10769 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10770 container->UpdateRanges();
7c9fdebe 10771
603f702b
JS
10772 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10773 // Layout() would stop prematurely at the top level.
10774 container->InvalidateHierarchy(GetRange());
5d7836c4 10775
7051fa41 10776 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10777
5912d19e
JS
10778 wxRichTextEvent cmdEvent(
10779 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10780 m_ctrl ? m_ctrl->GetId() : -1);
10781 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10782 cmdEvent.SetRange(GetRange());
10783 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10784 cmdEvent.SetContainer(container);
3e541562 10785
5912d19e
JS
10786 m_buffer->SendEvent(cmdEvent);
10787
5d7836c4
JS
10788 break;
10789 }
10790 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10791 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10792 {
10793 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
10794 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10795 // Layout() would stop prematurely at the top level.
10796 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10797
10798 UpdateAppearance(GetPosition());
10799
5912d19e 10800 wxRichTextEvent cmdEvent(
590a0f8b 10801 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
10802 m_ctrl ? m_ctrl->GetId() : -1);
10803 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10804 cmdEvent.SetRange(GetRange());
10805 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10806 cmdEvent.SetContainer(container);
3e541562 10807
5912d19e
JS
10808 m_buffer->SendEvent(cmdEvent);
10809
5d7836c4
JS
10810 break;
10811 }
603f702b
JS
10812 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10813 case wxRICHTEXT_CHANGE_OBJECT:
10814 {
10815 return Do();
10816 }
5d7836c4
JS
10817 default:
10818 break;
10819 }
10820
10821 return true;
10822}
10823
10824/// Update the control appearance
603f702b 10825void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 10826{
603f702b
JS
10827 wxRichTextParagraphLayoutBox* container = GetContainer();
10828 wxASSERT(container != NULL);
10829 if (!container)
10830 return;
10831
5d7836c4
JS
10832 if (m_ctrl)
10833 {
603f702b 10834 m_ctrl->SetFocusObject(container);
5d7836c4 10835 m_ctrl->SetCaretPosition(caretPosition);
603f702b 10836
5d7836c4
JS
10837 if (!m_ctrl->IsFrozen())
10838 {
603f702b
JS
10839 wxRect containerRect = container->GetRect();
10840
2f36e8dc 10841 m_ctrl->LayoutContent();
5d7836c4 10842
603f702b
JS
10843 // Refresh everything if there were floating objects or the container changed size
10844 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
e12b91a3 10845 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
603f702b
JS
10846 {
10847 m_ctrl->Refresh(false);
10848 }
10849 else
10850
10851#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10852 // Find refresh rectangle if we are in a position to optimise refresh
10853 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10854 {
10855 size_t i;
10856
4ba36292
JS
10857 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10858 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
603f702b
JS
10859
10860 // Start/end positions
10861 int firstY = 0;
10862 int lastY = firstVisiblePt.y + clientSize.y;
10863
10864 bool foundEnd = false;
10865
10866 // position offset - how many characters were inserted
10867 int positionOffset = GetRange().GetLength();
10868
10869 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10870 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10871 positionOffset = - positionOffset;
10872
10873 // find the first line which is being drawn at the same position as it was
10874 // before. Since we're talking about a simple insertion, we can assume
10875 // that the rest of the window does not need to be redrawn.
10876
10877 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10878 // Since we support floating layout, we should redraw the whole para instead of just
10879 // the first line touching the invalid range.
10880 if (para)
10881 {
10882 firstY = para->GetPosition().y;
10883 }
10884
10885 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10886 while (node)
10887 {
10888 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10889 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10890 while (node2)
10891 {
10892 wxRichTextLine* line = node2->GetData();
10893 wxPoint pt = line->GetAbsolutePosition();
10894 wxRichTextRange range = line->GetAbsoluteRange();
10895
10896 // we want to find the first line that is in the same position
10897 // as before. This will mean we're at the end of the changed text.
10898
10899 if (pt.y > lastY) // going past the end of the window, no more info
10900 {
10901 node2 = wxRichTextLineList::compatibility_iterator();
10902 node = wxRichTextObjectList::compatibility_iterator();
10903 }
10904 // Detect last line in the buffer
10905 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10906 {
10907 // If deleting text, make sure we refresh below as well as above
10908 if (positionOffset >= 0)
10909 {
10910 foundEnd = true;
10911 lastY = pt.y + line->GetSize().y;
10912 }
10913
10914 node2 = wxRichTextLineList::compatibility_iterator();
10915 node = wxRichTextObjectList::compatibility_iterator();
10916
10917 break;
10918 }
10919 else
10920 {
10921 // search for this line being at the same position as before
10922 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10923 {
10924 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10925 ((*optimizationLineYPositions)[i] == pt.y))
10926 {
10927 // Stop, we're now the same as we were
10928 foundEnd = true;
10929
10930 lastY = pt.y;
10931
10932 node2 = wxRichTextLineList::compatibility_iterator();
10933 node = wxRichTextObjectList::compatibility_iterator();
10934
10935 break;
10936 }
10937 }
10938 }
10939
10940 if (node2)
10941 node2 = node2->GetNext();
10942 }
10943
10944 if (node)
10945 node = node->GetNext();
10946 }
10947
10948 firstY = wxMax(firstVisiblePt.y, firstY);
10949 if (!foundEnd)
10950 lastY = firstVisiblePt.y + clientSize.y;
10951
10952 // Convert to device coordinates
4ba36292 10953 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
603f702b
JS
10954 m_ctrl->RefreshRect(rect);
10955 }
10956 else
1c13f06e 10957#endif
603f702b
JS
10958 m_ctrl->Refresh(false);
10959
10960 m_ctrl->PositionCaret();
4fe83b93
JS
10961
10962 // This causes styles to persist when doing programmatic
10963 // content creation except when Freeze/Thaw is used, so
10964 // disable this and check for the consequences.
10965 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 10966
5d7836c4 10967 if (sendUpdateEvent)
0ec1179b 10968 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 10969 }
7fe8059f 10970 }
5d7836c4
JS
10971}
10972
10973/// Replace the buffer paragraphs with the new ones.
0ca07313 10974void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 10975{
603f702b
JS
10976 wxRichTextParagraphLayoutBox* container = GetContainer();
10977 wxASSERT(container != NULL);
10978 if (!container)
10979 return;
10980
5d7836c4
JS
10981 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
10982 while (node)
10983 {
10984 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
10985 wxASSERT (para != NULL);
10986
10987 // We'll replace the existing paragraph by finding the paragraph at this position,
10988 // delete its node data, and setting a copy as the new node data.
10989 // TODO: make more efficient by simply swapping old and new paragraph objects.
10990
603f702b 10991 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
10992 if (existingPara)
10993 {
603f702b 10994 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
10995 if (bufferParaNode)
10996 {
10997 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 10998 newPara->SetParent(container);
5d7836c4
JS
10999
11000 bufferParaNode->SetData(newPara);
11001
11002 delete existingPara;
11003 }
11004 }
11005
11006 node = node->GetNext();
11007 }
11008}
11009
11010
11011/*!
11012 * wxRichTextRange
11013 * This stores beginning and end positions for a range of data.
11014 */
11015
603f702b
JS
11016WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11017
5d7836c4
JS
11018/// Limit this range to be within 'range'
11019bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11020{
11021 if (m_start < range.m_start)
11022 m_start = range.m_start;
11023
11024 if (m_end > range.m_end)
11025 m_end = range.m_end;
11026
11027 return true;
11028}
11029
11030/*!
11031 * wxRichTextImage implementation
11032 * This object represents an image.
11033 */
11034
bec80f4f 11035IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 11036
24777478 11037wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11038 wxRichTextObject(parent)
5d7836c4 11039{
23698b12 11040 Init();
cdaed652 11041 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
11042 if (charStyle)
11043 SetAttributes(*charStyle);
5d7836c4
JS
11044}
11045
24777478 11046wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11047 wxRichTextObject(parent)
5d7836c4 11048{
23698b12 11049 Init();
5d7836c4 11050 m_imageBlock = imageBlock;
4f32b3cf
JS
11051 if (charStyle)
11052 SetAttributes(*charStyle);
5d7836c4
JS
11053}
11054
23698b12
JS
11055void wxRichTextImage::Init()
11056{
11057 m_originalImageSize = wxSize(-1, -1);
11058}
11059
cdaed652
VZ
11060/// Create a cached image at the required size
11061bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
5d7836c4 11062{
23698b12
JS
11063 if (!m_imageBlock.IsOk())
11064 return false;
11065
11066 // If we have an original image size, use that to compute the cached bitmap size
11067 // instead of loading the image each time. This way we can avoid loading
11068 // the image so long as the new cached bitmap size hasn't changed.
11069
11070 wxImage image;
2798df59 11071 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
cdaed652 11072 {
23698b12 11073 m_imageCache = wxNullBitmap;
ce00f59b 11074
cdaed652
VZ
11075 m_imageBlock.Load(image);
11076 if (!image.IsOk())
11077 return false;
ce00f59b 11078
23698b12
JS
11079 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11080 }
11081
11082 int width = m_originalImageSize.GetWidth();
11083 int height = m_originalImageSize.GetHeight();
11084
11085 int parentWidth = 0;
11086 int parentHeight = 0;
bec80f4f 11087
23698b12
JS
11088 int maxWidth = -1;
11089 int maxHeight = -1;
11090
11091 wxRichTextBuffer* buffer = GetBuffer();
11092 if (buffer)
11093 {
11094 wxSize sz;
11095 if (buffer->GetRichTextCtrl())
cdaed652 11096 {
23698b12
JS
11097 // Subtract borders
11098 sz = buffer->GetRichTextCtrl()->GetClientSize();
11099
ab6b1860
JS
11100 // Use a minimum size to stop images becoming very small
11101 sz.x = wxMax(sz.x, 100);
11102 sz.y = wxMax(sz.y, 100);
11103
23698b12
JS
11104 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11105 marginRect = wxRect(0, 0, sz.x, sz.y);
11106 buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11107
11108 sz = contentRect.GetSize();
11109
11110 // Start with a maximum width of the control size, even if not specified by the content,
11111 // to minimize the amount of picture overlapping the right-hand side
11112 maxWidth = sz.x;
cdaed652 11113 }
23698b12
JS
11114 else
11115 sz = buffer->GetCachedSize();
11116 parentWidth = sz.GetWidth();
11117 parentHeight = sz.GetHeight();
11118 }
11119
11120 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11121 {
11122 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11123 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11124 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11125 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11126 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11127 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11128 }
11129
11130 // Limit to max width
11131
11132 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11133 {
11134 int mw = -1;
11135
11136 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11137 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11138 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11139 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11140 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11141 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11142
11143 // If we already have a smaller max width due to the constraints of the control size,
11144 // don't use the larger max width.
11145 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11146 maxWidth = mw;
11147 }
11148
11149 if (maxWidth > 0 && width > maxWidth)
11150 width = maxWidth;
11151
11152 // Preserve the aspect ratio
11153 if (width != m_originalImageSize.GetWidth())
11154 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11155
11156 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11157 {
11158 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11159 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11160 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11161 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11162 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11163 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11164
11165 // Preserve the aspect ratio
11166 if (height != m_originalImageSize.GetHeight())
11167 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11168 }
11169
11170 // Limit to max height
11171
11172 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11173 {
11174 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11175 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11176 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11177 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11178 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11179 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11180 }
11181
11182 if (maxHeight > 0 && height > maxHeight)
11183 {
11184 height = maxHeight;
11185
11186 // Preserve the aspect ratio
11187 if (height != m_originalImageSize.GetHeight())
11188 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11189 }
11190
ab6b1860
JS
11191 // Prevent the use of zero size
11192 width = wxMax(1, width);
11193 height = wxMax(1, height);
11194
23698b12
JS
11195 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11196 {
11197 // Do nothing, we didn't need to change the image cache
11198 }
11199 else
11200 {
11201 if (!image.IsOk())
cdaed652 11202 {
23698b12
JS
11203 m_imageBlock.Load(image);
11204 if (!image.IsOk())
11205 return false;
cdaed652 11206 }
5d7836c4 11207
cdaed652
VZ
11208 if (image.GetWidth() == width && image.GetHeight() == height)
11209 m_imageCache = wxBitmap(image);
11210 else
11211 {
11212 // If the original width and height is small, e.g. 400 or below,
11213 // scale up and then down to improve image quality. This can make
11214 // a big difference, with not much performance hit.
11215 int upscaleThreshold = 400;
11216 wxImage img;
11217 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11218 {
11219 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11220 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11221 }
11222 else
11223 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11224 m_imageCache = wxBitmap(img);
11225 }
11226 }
ce00f59b 11227
cdaed652 11228 return m_imageCache.IsOk();
5d7836c4
JS
11229}
11230
5d7836c4 11231/// Draw the item
20d09da5 11232bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 11233{
603f702b
JS
11234 if (!IsShown())
11235 return true;
11236
cdaed652
VZ
11237 // Don't need cached size AFAIK
11238 // wxSize size = GetCachedSize();
11239 if (!LoadImageCache(dc))
5d7836c4 11240 return false;
ce00f59b 11241
8db2e3ef
JS
11242 wxRichTextAttr attr(GetAttributes());
11243 context.ApplyVirtualAttributes(attr, this);
11244
11245 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b 11246
603f702b
JS
11247 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11248 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11249 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 11250 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11251
11252 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 11253
a70eb13e 11254 if (selection.WithinSelection(GetRange().GetStart(), this))
5d7836c4 11255 {
ecb5fbf1
JS
11256 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11257 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 11258 dc.SetLogicalFunction(wxINVERT);
603f702b 11259 dc.DrawRectangle(contentRect);
5d7836c4
JS
11260 dc.SetLogicalFunction(wxCOPY);
11261 }
11262
11263 return true;
11264}
11265
11266/// Lay the item out
8db2e3ef 11267bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 11268{
cdaed652
VZ
11269 if (!LoadImageCache(dc))
11270 return false;
5d7836c4 11271
603f702b
JS
11272 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11273 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11274 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
11275
11276 wxRichTextAttr attr(GetAttributes());
11277 context.ApplyVirtualAttributes(attr, this);
11278
11279 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11280
11281 wxSize overallSize = marginRect.GetSize();
11282
11283 SetCachedSize(overallSize);
11284 SetMaxSize(overallSize);
11285 SetMinSize(overallSize);
cdaed652 11286 SetPosition(rect.GetPosition());
5d7836c4
JS
11287
11288 return true;
11289}
11290
11291/// Get/set the object size for the given range. Returns false if the range
11292/// is invalid for this object.
8db2e3ef 11293bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
5d7836c4
JS
11294{
11295 if (!range.IsWithin(GetRange()))
11296 return false;
11297
cdaed652 11298 if (!((wxRichTextImage*)this)->LoadImageCache(dc))
31778480 11299 {
cdaed652
VZ
11300 size.x = 0; size.y = 0;
11301 if (partialExtents)
31778480 11302 partialExtents->Add(0);
cdaed652 11303 return false;
31778480 11304 }
ce00f59b 11305
8db2e3ef
JS
11306 wxRichTextAttr attr(GetAttributes());
11307 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11308
603f702b
JS
11309 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11310 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11311 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef 11312 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11313
11314 wxSize overallSize = marginRect.GetSize();
31778480 11315
cdaed652 11316 if (partialExtents)
603f702b 11317 partialExtents->Add(overallSize.x);
5d7836c4 11318
603f702b 11319 size = overallSize;
5d7836c4
JS
11320
11321 return true;
11322}
11323
603f702b
JS
11324// Get the 'natural' size for an object. For an image, it would be the
11325// image size.
11326wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11327{
11328 wxTextAttrSize size;
11329 if (GetImageCache().IsOk())
11330 {
11331 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11332 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11333 }
11334 return size;
11335}
11336
11337
5d7836c4
JS
11338/// Copy
11339void wxRichTextImage::Copy(const wxRichTextImage& obj)
11340{
bec80f4f 11341 wxRichTextObject::Copy(obj);
59509217 11342
5d7836c4 11343 m_imageBlock = obj.m_imageBlock;
23698b12 11344 m_originalImageSize = obj.m_originalImageSize;
5d7836c4
JS
11345}
11346
cdaed652
VZ
11347/// Edit properties via a GUI
11348bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11349{
603f702b
JS
11350 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11351 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
11352
11353 if (imageDlg.ShowModal() == wxID_OK)
11354 {
603f702b
JS
11355 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11356 // indeterminate in the object.
11357 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
11358 return true;
11359 }
11360 else
11361 return false;
11362}
11363
5d7836c4
JS
11364/*!
11365 * Utilities
11366 *
11367 */
11368
11369/// Compare two attribute objects
24777478 11370bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 11371{
38f833b1 11372 return (attr1 == attr2);
5d7836c4
JS
11373}
11374
44cc96a8
JS
11375/// Compare tabs
11376bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11377{
11378 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
11379 return false;
11380
44cc96a8
JS
11381 size_t i;
11382 for (i = 0; i < tabs1.GetCount(); i++)
11383 {
11384 if (tabs1[i] != tabs2[i])
11385 return false;
11386 }
11387 return true;
11388}
5d7836c4 11389
24777478 11390bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
11391{
11392 return destStyle.Apply(style, compareWith);
11393}
5d7836c4 11394
44cc96a8 11395// Remove attributes
24777478 11396bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 11397{
24777478 11398 return destStyle.RemoveStyle(style);
44cc96a8 11399}
5d7836c4 11400
44cc96a8
JS
11401/// Combine two bitlists, specifying the bits of interest with separate flags.
11402bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11403{
24777478 11404 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 11405}
5d7836c4 11406
44cc96a8
JS
11407/// Compare two bitlists
11408bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11409{
24777478 11410 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 11411}
38f833b1 11412
44cc96a8 11413/// Split into paragraph and character styles
24777478 11414bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 11415{
24777478 11416 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 11417}
5d7836c4 11418
44cc96a8
JS
11419/// Convert a decimal to Roman numerals
11420wxString wxRichTextDecimalToRoman(long n)
11421{
11422 static wxArrayInt decimalNumbers;
11423 static wxArrayString romanNumbers;
5d7836c4 11424
44cc96a8
JS
11425 // Clean up arrays
11426 if (n == -1)
11427 {
11428 decimalNumbers.Clear();
11429 romanNumbers.Clear();
11430 return wxEmptyString;
11431 }
5d7836c4 11432
44cc96a8
JS
11433 if (decimalNumbers.GetCount() == 0)
11434 {
11435 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 11436
44cc96a8
JS
11437 wxRichTextAddDecRom(1000, wxT("M"));
11438 wxRichTextAddDecRom(900, wxT("CM"));
11439 wxRichTextAddDecRom(500, wxT("D"));
11440 wxRichTextAddDecRom(400, wxT("CD"));
11441 wxRichTextAddDecRom(100, wxT("C"));
11442 wxRichTextAddDecRom(90, wxT("XC"));
11443 wxRichTextAddDecRom(50, wxT("L"));
11444 wxRichTextAddDecRom(40, wxT("XL"));
11445 wxRichTextAddDecRom(10, wxT("X"));
11446 wxRichTextAddDecRom(9, wxT("IX"));
11447 wxRichTextAddDecRom(5, wxT("V"));
11448 wxRichTextAddDecRom(4, wxT("IV"));
11449 wxRichTextAddDecRom(1, wxT("I"));
11450 }
5d7836c4 11451
44cc96a8
JS
11452 int i = 0;
11453 wxString roman;
ea160b2e 11454
44cc96a8 11455 while (n > 0 && i < 13)
42688aea 11456 {
44cc96a8
JS
11457 if (n >= decimalNumbers[i])
11458 {
11459 n -= decimalNumbers[i];
11460 roman += romanNumbers[i];
11461 }
11462 else
11463 {
11464 i ++;
11465 }
42688aea 11466 }
44cc96a8
JS
11467 if (roman.IsEmpty())
11468 roman = wxT("0");
11469 return roman;
11470}
42688aea 11471
44cc96a8
JS
11472/*!
11473 * wxRichTextFileHandler
11474 * Base class for file handlers
11475 */
4d6d8bf4 11476
44cc96a8 11477IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 11478
44cc96a8
JS
11479#if wxUSE_FFILE && wxUSE_STREAMS
11480bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 11481{
44cc96a8 11482 wxFFileInputStream stream(filename);
a1b806b9 11483 if (stream.IsOk())
44cc96a8 11484 return LoadFile(buffer, stream);
5d7836c4 11485
44cc96a8
JS
11486 return false;
11487}
5d7836c4 11488
44cc96a8
JS
11489bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11490{
11491 wxFFileOutputStream stream(filename);
a1b806b9 11492 if (stream.IsOk())
44cc96a8 11493 return SaveFile(buffer, stream);
5d7836c4 11494
44cc96a8
JS
11495 return false;
11496}
11497#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 11498
44cc96a8
JS
11499/// Can we handle this filename (if using files)? By default, checks the extension.
11500bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11501{
11502 wxString path, file, ext;
a51e601e 11503 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 11504
44cc96a8
JS
11505 return (ext.Lower() == GetExtension());
11506}
5d7836c4 11507
44cc96a8
JS
11508/*!
11509 * wxRichTextTextHandler
11510 * Plain text handler
11511 */
5d7836c4 11512
44cc96a8 11513IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 11514
44cc96a8
JS
11515#if wxUSE_STREAMS
11516bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11517{
11518 if (!stream.IsOk())
797e38dd
JS
11519 return false;
11520
44cc96a8
JS
11521 wxString str;
11522 int lastCh = 0;
5d7836c4 11523
44cc96a8
JS
11524 while (!stream.Eof())
11525 {
11526 int ch = stream.GetC();
5d7836c4 11527
44cc96a8
JS
11528 if (!stream.Eof())
11529 {
11530 if (ch == 10 && lastCh != 13)
11531 str += wxT('\n');
5d7836c4 11532
44cc96a8
JS
11533 if (ch > 0 && ch != 10)
11534 str += wxChar(ch);
5d7836c4 11535
44cc96a8
JS
11536 lastCh = ch;
11537 }
11538 }
5d7836c4 11539
44cc96a8
JS
11540 buffer->ResetAndClearCommands();
11541 buffer->Clear();
11542 buffer->AddParagraphs(str);
11543 buffer->UpdateRanges();
5d7836c4 11544
44cc96a8
JS
11545 return true;
11546}
5d7836c4 11547
44cc96a8
JS
11548bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11549{
11550 if (!stream.IsOk())
5d7836c4
JS
11551 return false;
11552
44cc96a8 11553 wxString text = buffer->GetText();
38f833b1 11554
44cc96a8
JS
11555 wxString newLine = wxRichTextLineBreakChar;
11556 text.Replace(newLine, wxT("\n"));
5d7836c4 11557
44cc96a8 11558 wxCharBuffer buf = text.ToAscii();
5d7836c4 11559
44cc96a8
JS
11560 stream.Write((const char*) buf, text.length());
11561 return true;
11562}
11563#endif // wxUSE_STREAMS
5d7836c4 11564
44cc96a8
JS
11565/*
11566 * Stores information about an image, in binary in-memory form
11567 */
59509217 11568
44cc96a8
JS
11569wxRichTextImageBlock::wxRichTextImageBlock()
11570{
11571 Init();
11572}
5d7836c4 11573
44cc96a8
JS
11574wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11575{
11576 Init();
11577 Copy(block);
11578}
ea160b2e 11579
44cc96a8
JS
11580wxRichTextImageBlock::~wxRichTextImageBlock()
11581{
5276b0a5 11582 wxDELETEA(m_data);
5d7836c4
JS
11583}
11584
44cc96a8 11585void wxRichTextImageBlock::Init()
5d7836c4
JS
11586{
11587 m_data = NULL;
11588 m_dataSize = 0;
d75a69e8 11589 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11590}
11591
11592void wxRichTextImageBlock::Clear()
11593{
5276b0a5 11594 wxDELETEA(m_data);
5d7836c4 11595 m_dataSize = 0;
d75a69e8 11596 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11597}
11598
11599
11600// Load the original image into a memory block.
11601// If the image is not a JPEG, we must convert it into a JPEG
11602// to conserve space.
11603// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11604// load the image a 2nd time.
11605
d75a69e8
FM
11606bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11607 wxImage& image, bool convertToJPEG)
5d7836c4
JS
11608{
11609 m_imageType = imageType;
11610
11611 wxString filenameToRead(filename);
7fe8059f 11612 bool removeFile = false;
5d7836c4 11613
62891c87 11614 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11615 return false; // Could not determine image type
5d7836c4
JS
11616
11617 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11618 {
a51e601e
FM
11619 wxString tempFile =
11620 wxFileName::CreateTempFileName(_("image"));
5d7836c4 11621
a51e601e 11622 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11623
11624 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11625 filenameToRead = tempFile;
7fe8059f 11626 removeFile = true;
5d7836c4
JS
11627
11628 m_imageType = wxBITMAP_TYPE_JPEG;
11629 }
11630 wxFile file;
11631 if (!file.Open(filenameToRead))
7fe8059f 11632 return false;
5d7836c4
JS
11633
11634 m_dataSize = (size_t) file.Length();
11635 file.Close();
11636
11637 if (m_data)
11638 delete[] m_data;
11639 m_data = ReadBlock(filenameToRead, m_dataSize);
11640
11641 if (removeFile)
11642 wxRemoveFile(filenameToRead);
11643
11644 return (m_data != NULL);
11645}
11646
11647// Make an image block from the wxImage in the given
11648// format.
d75a69e8 11649bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 11650{
5d7836c4
JS
11651 image.SetOption(wxT("quality"), quality);
11652
62891c87 11653 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11654 return false; // Could not determine image type
5d7836c4 11655
cdaed652
VZ
11656 return DoMakeImageBlock(image, imageType);
11657}
11658
11659// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11660bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11661{
11662 if (imageType == wxBITMAP_TYPE_INVALID)
11663 return false; // Could not determine image type
ce00f59b 11664
cdaed652
VZ
11665 return DoMakeImageBlock(image, imageType);
11666}
7fe8059f 11667
cdaed652
VZ
11668// Makes the image block
11669bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11670{
11671 wxMemoryOutputStream memStream;
11672 if (!image.SaveFile(memStream, imageType))
5d7836c4 11673 {
7fe8059f 11674 return false;
5d7836c4 11675 }
ce00f59b 11676
cdaed652
VZ
11677 unsigned char* block = new unsigned char[memStream.GetSize()];
11678 if (!block)
377c1ba4 11679 return false;
ce00f59b 11680
5d7836c4
JS
11681 if (m_data)
11682 delete[] m_data;
cdaed652 11683 m_data = block;
ce00f59b
VZ
11684
11685 m_imageType = imageType;
cdaed652 11686 m_dataSize = memStream.GetSize();
5d7836c4 11687
cdaed652 11688 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
11689
11690 return (m_data != NULL);
11691}
11692
5d7836c4
JS
11693// Write to a file
11694bool wxRichTextImageBlock::Write(const wxString& filename)
11695{
11696 return WriteBlock(filename, m_data, m_dataSize);
11697}
11698
11699void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11700{
11701 m_imageType = block.m_imageType;
5276b0a5 11702 wxDELETEA(m_data);
5d7836c4
JS
11703 m_dataSize = block.m_dataSize;
11704 if (m_dataSize == 0)
11705 return;
11706
11707 m_data = new unsigned char[m_dataSize];
11708 unsigned int i;
11709 for (i = 0; i < m_dataSize; i++)
11710 m_data[i] = block.m_data[i];
11711}
11712
11713//// Operators
11714void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11715{
11716 Copy(block);
11717}
11718
11719// Load a wxImage from the block
11720bool wxRichTextImageBlock::Load(wxImage& image)
11721{
11722 if (!m_data)
7fe8059f 11723 return false;
5d7836c4
JS
11724
11725 // Read in the image.
0ca07313 11726#if wxUSE_STREAMS
5d7836c4
JS
11727 wxMemoryInputStream mstream(m_data, m_dataSize);
11728 bool success = image.LoadFile(mstream, GetImageType());
11729#else
a51e601e
FM
11730 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11731 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11732
11733 if (!WriteBlock(tempFile, m_data, m_dataSize))
11734 {
7fe8059f 11735 return false;
5d7836c4
JS
11736 }
11737 success = image.LoadFile(tempFile, GetImageType());
11738 wxRemoveFile(tempFile);
11739#endif
11740
11741 return success;
11742}
11743
11744// Write data in hex to a stream
11745bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11746{
4dc7ae1a
JS
11747 if (m_dataSize == 0)
11748 return true;
11749
11750 int bufSize = 100000;
a3c12576
JS
11751 if (int(2*m_dataSize) < bufSize)
11752 bufSize = 2*m_dataSize;
4dc7ae1a 11753 char* buf = new char[bufSize+1];
351c0647
JS
11754
11755 int left = m_dataSize;
11756 int n, i, j;
11757 j = 0;
11758 while (left > 0)
5d7836c4 11759 {
351c0647
JS
11760 if (left*2 > bufSize)
11761 {
11762 n = bufSize; left -= (bufSize/2);
11763 }
11764 else
11765 {
11766 n = left*2; left = 0;
11767 }
7fe8059f 11768
351c0647
JS
11769 char* b = buf;
11770 for (i = 0; i < (n/2); i++)
11771 {
f728025e 11772 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
11773 b += 2; j ++;
11774 }
5d7836c4 11775
351c0647
JS
11776 buf[n] = 0;
11777 stream.Write((const char*) buf, n);
11778 }
4dc7ae1a 11779 delete[] buf;
5d7836c4
JS
11780 return true;
11781}
11782
11783// Read data in hex from a stream
d75a69e8 11784bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
11785{
11786 int dataSize = length/2;
11787
11788 if (m_data)
11789 delete[] m_data;
11790
046fce47
FM
11791 // create a null terminated temporary string:
11792 char str[3];
11793 str[2] = '\0';
11794
5d7836c4
JS
11795 m_data = new unsigned char[dataSize];
11796 int i;
11797 for (i = 0; i < dataSize; i ++)
11798 {
c9f78968
VS
11799 str[0] = (char)stream.GetC();
11800 str[1] = (char)stream.GetC();
5d7836c4 11801
a9465653 11802 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
11803 }
11804
11805 m_dataSize = dataSize;
11806 m_imageType = imageType;
11807
11808 return true;
11809}
11810
5d7836c4
JS
11811// Allocate and read from stream as a block of memory
11812unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11813{
11814 unsigned char* block = new unsigned char[size];
11815 if (!block)
11816 return NULL;
11817
11818 stream.Read(block, size);
11819
11820 return block;
11821}
11822
11823unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11824{
11825 wxFileInputStream stream(filename);
a1b806b9 11826 if (!stream.IsOk())
5d7836c4
JS
11827 return NULL;
11828
11829 return ReadBlock(stream, size);
11830}
11831
11832// Write memory block to stream
11833bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11834{
11835 stream.Write((void*) block, size);
11836 return stream.IsOk();
11837
11838}
11839
11840// Write memory block to file
11841bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11842{
11843 wxFileOutputStream outStream(filename);
a1b806b9 11844 if (!outStream.IsOk())
7fe8059f 11845 return false;
5d7836c4
JS
11846
11847 return WriteBlock(outStream, block, size);
11848}
11849
d2d0adc7
JS
11850// Gets the extension for the block's type
11851wxString wxRichTextImageBlock::GetExtension() const
11852{
11853 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11854 if (handler)
11855 return handler->GetExtension();
11856 else
11857 return wxEmptyString;
11858}
11859
0ca07313
JS
11860#if wxUSE_DATAOBJ
11861
11862/*!
11863 * The data object for a wxRichTextBuffer
11864 */
11865
11866const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11867
11868wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11869{
11870 m_richTextBuffer = richTextBuffer;
11871
11872 // this string should uniquely identify our format, but is otherwise
11873 // arbitrary
11874 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11875
11876 SetFormat(m_formatRichTextBuffer);
11877}
11878
11879wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11880{
11881 delete m_richTextBuffer;
11882}
11883
11884// after a call to this function, the richTextBuffer is owned by the caller and it
11885// is responsible for deleting it!
11886wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11887{
11888 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11889 m_richTextBuffer = NULL;
11890
11891 return richTextBuffer;
11892}
11893
11894wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11895{
11896 return m_formatRichTextBuffer;
11897}
11898
11899size_t wxRichTextBufferDataObject::GetDataSize() const
11900{
11901 if (!m_richTextBuffer)
11902 return 0;
11903
11904 wxString bufXML;
11905
11906 {
11907 wxStringOutputStream stream(& bufXML);
11908 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11909 {
11910 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11911 return 0;
11912 }
11913 }
11914
11915#if wxUSE_UNICODE
11916 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11917 return strlen(buffer) + 1;
11918#else
11919 return bufXML.Length()+1;
11920#endif
11921}
11922
11923bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11924{
11925 if (!pBuf || !m_richTextBuffer)
11926 return false;
11927
11928 wxString bufXML;
11929
11930 {
11931 wxStringOutputStream stream(& bufXML);
11932 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11933 {
11934 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11935 return 0;
11936 }
11937 }
11938
11939#if wxUSE_UNICODE
11940 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11941 size_t len = strlen(buffer);
11942 memcpy((char*) pBuf, (const char*) buffer, len);
11943 ((char*) pBuf)[len] = 0;
11944#else
11945 size_t len = bufXML.Length();
11946 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11947 ((char*) pBuf)[len] = 0;
11948#endif
11949
11950 return true;
11951}
11952
11953bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
11954{
5276b0a5 11955 wxDELETE(m_richTextBuffer);
0ca07313
JS
11956
11957 wxString bufXML((const char*) buf, wxConvUTF8);
11958
11959 m_richTextBuffer = new wxRichTextBuffer;
11960
11961 wxStringInputStream stream(bufXML);
11962 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
11963 {
11964 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11965
5276b0a5 11966 wxDELETE(m_richTextBuffer);
0ca07313
JS
11967
11968 return false;
11969 }
11970 return true;
11971}
11972
11973#endif
11974 // wxUSE_DATAOBJ
11975
44cc96a8
JS
11976
11977/*
11978 * wxRichTextFontTable
11979 * Manages quick access to a pool of fonts for rendering rich text
11980 */
11981
d65381ac 11982WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
11983
11984class wxRichTextFontTableData: public wxObjectRefData
11985{
11986public:
11987 wxRichTextFontTableData() {}
11988
32423dd8 11989 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
44cc96a8
JS
11990
11991 wxRichTextFontTableHashMap m_hashMap;
11992};
11993
32423dd8 11994wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
44cc96a8
JS
11995{
11996 wxString facename(fontSpec.GetFontFaceName());
44cc96a8 11997
32423dd8
JS
11998 int fontSize = fontSpec.GetFontSize();
11999 if (fontScale != 1.0)
12000 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12001
12002 wxString units;
12003 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12004 units = wxT("px");
12005 else
12006 units = wxT("pt");
12007 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12008 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12009 facename.c_str(), (int) fontSpec.GetFontEncoding());
12010
12011 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
44cc96a8
JS
12012 if ( entry == m_hashMap.end() )
12013 {
32423dd8
JS
12014 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12015 {
b42e4b3e 12016 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
32423dd8
JS
12017 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12018 font.SetStrikethrough(true);
12019 m_hashMap[spec] = font;
12020 return font;
12021 }
12022 else
12023 {
5a0e33af 12024 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
32423dd8
JS
12025 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12026 font.SetStrikethrough(true);
12027
12028 m_hashMap[spec] = font;
12029 return font;
12030 }
44cc96a8
JS
12031 }
12032 else
12033 {
12034 return entry->second;
12035 }
12036}
12037
12038IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12039
12040wxRichTextFontTable::wxRichTextFontTable()
12041{
12042 m_refData = new wxRichTextFontTableData;
32423dd8 12043 m_fontScale = 1.0;
44cc96a8
JS
12044}
12045
12046wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 12047 : wxObject()
44cc96a8
JS
12048{
12049 (*this) = table;
12050}
12051
12052wxRichTextFontTable::~wxRichTextFontTable()
12053{
12054 UnRef();
12055}
12056
12057bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12058{
12059 return (m_refData == table.m_refData);
12060}
12061
12062void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12063{
12064 Ref(table);
32423dd8 12065 m_fontScale = table.m_fontScale;
44cc96a8
JS
12066}
12067
24777478 12068wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
12069{
12070 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12071 if (data)
32423dd8 12072 return data->FindFont(fontSpec, m_fontScale);
44cc96a8
JS
12073 else
12074 return wxFont();
12075}
12076
12077void wxRichTextFontTable::Clear()
12078{
12079 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12080 if (data)
12081 data->m_hashMap.clear();
12082}
12083
32423dd8
JS
12084void wxRichTextFontTable::SetFontScale(double fontScale)
12085{
12086 if (fontScale != m_fontScale)
12087 Clear();
12088 m_fontScale = fontScale;
12089}
12090
24777478
JS
12091// wxTextBoxAttr
12092
24777478
JS
12093void wxTextBoxAttr::Reset()
12094{
12095 m_flags = 0;
603f702b
JS
12096 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12097 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12098 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12099 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 12100 m_boxStyleName = wxEmptyString;
bec80f4f 12101
24777478
JS
12102 m_margins.Reset();
12103 m_padding.Reset();
12104 m_position.Reset();
12105
603f702b 12106 m_size.Reset();
303f0be7
JS
12107 m_minSize.Reset();
12108 m_maxSize.Reset();
24777478
JS
12109
12110 m_border.Reset();
12111 m_outline.Reset();
12112}
12113
12114// Equality test
12115bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12116{
12117 return (
12118 m_flags == attr.m_flags &&
12119 m_floatMode == attr.m_floatMode &&
12120 m_clearMode == attr.m_clearMode &&
12121 m_collapseMode == attr.m_collapseMode &&
603f702b 12122 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 12123
24777478
JS
12124 m_margins == attr.m_margins &&
12125 m_padding == attr.m_padding &&
12126 m_position == attr.m_position &&
12127
603f702b 12128 m_size == attr.m_size &&
303f0be7
JS
12129 m_minSize == attr.m_minSize &&
12130 m_maxSize == attr.m_maxSize &&
24777478
JS
12131
12132 m_border == attr.m_border &&
2f987d83
JS
12133 m_outline == attr.m_outline &&
12134
12135 m_boxStyleName == attr.m_boxStyleName
24777478
JS
12136 );
12137}
12138
12139// Partial equality test
32423dd8 12140bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
24777478 12141{
32423dd8
JS
12142 if (!weakTest &&
12143 ((!HasFloatMode() && attr.HasFloatMode()) ||
12144 (!HasClearMode() && attr.HasClearMode()) ||
12145 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12146 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12147 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12148 {
12149 return false;
12150 }
24777478
JS
12151 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12152 return false;
12153
12154 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12155 return false;
12156
12157 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12158 return false;
12159
603f702b
JS
12160 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12161 return false;
12162
2f987d83
JS
12163 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12164 return false;
12165
24777478
JS
12166 // Position
12167
32423dd8 12168 if (!m_position.EqPartial(attr.m_position, weakTest))
24777478
JS
12169 return false;
12170
303f0be7
JS
12171 // Size
12172
32423dd8 12173 if (!m_size.EqPartial(attr.m_size, weakTest))
303f0be7 12174 return false;
32423dd8 12175 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
303f0be7 12176 return false;
32423dd8 12177 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
303f0be7
JS
12178 return false;
12179
24777478
JS
12180 // Margins
12181
32423dd8 12182 if (!m_margins.EqPartial(attr.m_margins, weakTest))
24777478
JS
12183 return false;
12184
12185 // Padding
12186
32423dd8 12187 if (!m_padding.EqPartial(attr.m_padding, weakTest))
24777478
JS
12188 return false;
12189
12190 // Border
12191
32423dd8 12192 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
24777478
JS
12193 return false;
12194
12195 // Outline
12196
32423dd8 12197 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
24777478
JS
12198 return false;
12199
12200 return true;
12201}
12202
12203// Merges the given attributes. If compareWith
12204// is non-NULL, then it will be used to mask out those attributes that are the same in style
12205// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12206bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12207{
12208 if (attr.HasFloatMode())
12209 {
12210 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12211 SetFloatMode(attr.GetFloatMode());
12212 }
12213
12214 if (attr.HasClearMode())
12215 {
12216 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12217 SetClearMode(attr.GetClearMode());
12218 }
12219
12220 if (attr.HasCollapseBorders())
12221 {
12222 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
12223 SetCollapseBorders(attr.GetCollapseBorders());
12224 }
12225
12226 if (attr.HasVerticalAlignment())
12227 {
12228 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12229 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 12230 }
bec80f4f 12231
2f987d83
JS
12232 if (attr.HasBoxStyleName())
12233 {
12234 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12235 SetBoxStyleName(attr.GetBoxStyleName());
12236 }
12237
bec80f4f
JS
12238 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12239 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12240 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 12241
603f702b 12242 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
12243 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12244 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 12245
bec80f4f
JS
12246 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12247 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
12248
12249 return true;
12250}
12251
12252// Remove specified attributes from this object
12253bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12254{
12255 if (attr.HasFloatMode())
12256 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12257
12258 if (attr.HasClearMode())
12259 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12260
12261 if (attr.HasCollapseBorders())
12262 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12263
603f702b
JS
12264 if (attr.HasVerticalAlignment())
12265 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12266
2f987d83
JS
12267 if (attr.HasBoxStyleName())
12268 {
12269 SetBoxStyleName(wxEmptyString);
12270 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12271 }
12272
24777478
JS
12273 m_margins.RemoveStyle(attr.m_margins);
12274 m_padding.RemoveStyle(attr.m_padding);
12275 m_position.RemoveStyle(attr.m_position);
12276
603f702b 12277 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
12278 m_minSize.RemoveStyle(attr.m_minSize);
12279 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
12280
12281 m_border.RemoveStyle(attr.m_border);
12282 m_outline.RemoveStyle(attr.m_outline);
12283
12284 return true;
12285}
12286
12287// Collects the attributes that are common to a range of content, building up a note of
12288// which attributes are absent in some objects and which clash in some objects.
12289void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12290{
12291 if (attr.HasFloatMode())
12292 {
12293 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12294 {
12295 if (HasFloatMode())
12296 {
12297 if (GetFloatMode() != attr.GetFloatMode())
12298 {
12299 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12300 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12301 }
12302 }
12303 else
12304 SetFloatMode(attr.GetFloatMode());
12305 }
12306 }
12307 else
12308 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 12309
24777478
JS
12310 if (attr.HasClearMode())
12311 {
12312 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12313 {
12314 if (HasClearMode())
12315 {
12316 if (GetClearMode() != attr.GetClearMode())
12317 {
12318 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12319 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12320 }
12321 }
12322 else
12323 SetClearMode(attr.GetClearMode());
12324 }
12325 }
12326 else
12327 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12328
12329 if (attr.HasCollapseBorders())
12330 {
12331 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12332 {
12333 if (HasCollapseBorders())
12334 {
12335 if (GetCollapseBorders() != attr.GetCollapseBorders())
12336 {
12337 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12338 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12339 }
12340 }
12341 else
12342 SetCollapseBorders(attr.GetCollapseBorders());
12343 }
12344 }
12345 else
12346 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 12347
603f702b
JS
12348 if (attr.HasVerticalAlignment())
12349 {
12350 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12351 {
12352 if (HasVerticalAlignment())
12353 {
12354 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12355 {
12356 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12357 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12358 }
12359 }
12360 else
12361 SetVerticalAlignment(attr.GetVerticalAlignment());
12362 }
12363 }
12364 else
12365 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12366
2f987d83
JS
12367 if (attr.HasBoxStyleName())
12368 {
12369 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12370 {
12371 if (HasBoxStyleName())
12372 {
12373 if (GetBoxStyleName() != attr.GetBoxStyleName())
12374 {
12375 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12376 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12377 }
12378 }
12379 else
12380 SetBoxStyleName(attr.GetBoxStyleName());
12381 }
12382 }
12383 else
12384 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12385
24777478
JS
12386 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12387 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12388 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12389
603f702b 12390 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
12391 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12392 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
12393
12394 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12395 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12396}
12397
eb3d8a33
JS
12398bool wxTextBoxAttr::IsDefault() const
12399{
12400 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 12401 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
12402 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12403}
12404
24777478
JS
12405// wxRichTextAttr
12406
12407void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12408{
bec80f4f
JS
12409 wxTextAttr::Copy(attr);
12410
24777478
JS
12411 m_textBoxAttr = attr.m_textBoxAttr;
12412}
12413
12414bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12415{
12416 if (!(wxTextAttr::operator==(attr)))
12417 return false;
bec80f4f 12418
24777478
JS
12419 return (m_textBoxAttr == attr.m_textBoxAttr);
12420}
12421
32423dd8
JS
12422// Partial equality test
12423bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
24777478 12424{
32423dd8 12425 if (!(wxTextAttr::EqPartial(attr, weakTest)))
24777478 12426 return false;
bec80f4f 12427
32423dd8 12428 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
24777478
JS
12429}
12430
12431// Merges the given attributes. If compareWith
12432// is non-NULL, then it will be used to mask out those attributes that are the same in style
12433// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12434bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12435{
12436 wxTextAttr::Apply(style, compareWith);
12437
12438 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12439}
12440
12441// Remove specified attributes from this object
12442bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12443{
12444 wxTextAttr::RemoveStyle(*this, attr);
12445
12446 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12447}
12448
12449// Collects the attributes that are common to a range of content, building up a note of
12450// which attributes are absent in some objects and which clash in some objects.
12451void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12452{
12453 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 12454
24777478
JS
12455 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12456}
12457
12458// Partial equality test
32423dd8 12459bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
24777478 12460{
32423dd8
JS
12461 if (!weakTest &&
12462 ((!HasStyle() && border.HasStyle()) ||
12463 (!HasColour() && border.HasColour()) ||
12464 (!HasWidth() && border.HasWidth())))
12465 {
12466 return false;
12467 }
12468
12469 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
24777478
JS
12470 return false;
12471
32423dd8 12472 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
24777478
JS
12473 return false;
12474
32423dd8 12475 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
24777478
JS
12476 return false;
12477
12478 return true;
12479}
12480
12481// Apply border to 'this', but not if the same as compareWith
bec80f4f 12482bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
12483{
12484 if (border.HasStyle())
12485 {
12486 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12487 SetStyle(border.GetStyle());
12488 }
12489 if (border.HasColour())
12490 {
12491 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12492 SetColour(border.GetColourLong());
12493 }
12494 if (border.HasWidth())
12495 {
12496 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12497 SetWidth(border.GetWidth());
12498 }
12499
12500 return true;
12501}
12502
12503// Remove specified attributes from this object
bec80f4f 12504bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
12505{
12506 if (attr.HasStyle() && HasStyle())
12507 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12508 if (attr.HasColour() && HasColour())
12509 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12510 if (attr.HasWidth() && HasWidth())
12511 m_borderWidth.Reset();
12512
12513 return true;
12514}
12515
12516// Collects the attributes that are common to a range of content, building up a note of
12517// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12518void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
12519{
12520 if (attr.HasStyle())
12521 {
12522 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12523 {
12524 if (HasStyle())
12525 {
12526 if (GetStyle() != attr.GetStyle())
12527 {
12528 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12529 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12530 }
12531 }
12532 else
12533 SetStyle(attr.GetStyle());
12534 }
12535 }
12536 else
12537 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12538
12539 if (attr.HasColour())
12540 {
12541 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12542 {
12543 if (HasColour())
12544 {
12545 if (GetColour() != attr.GetColour())
12546 {
12547 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12548 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12549 }
12550 }
12551 else
12552 SetColour(attr.GetColourLong());
12553 }
12554 }
12555 else
12556 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 12557
24777478
JS
12558 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12559}
12560
12561// Partial equality test
32423dd8 12562bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
24777478 12563{
32423dd8
JS
12564 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12565 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
24777478
JS
12566}
12567
12568// Apply border to 'this', but not if the same as compareWith
bec80f4f 12569bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 12570{
bec80f4f
JS
12571 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12572 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12573 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12574 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
12575 return true;
12576}
12577
12578// Remove specified attributes from this object
bec80f4f 12579bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
12580{
12581 m_left.RemoveStyle(attr.m_left);
12582 m_right.RemoveStyle(attr.m_right);
12583 m_top.RemoveStyle(attr.m_top);
12584 m_bottom.RemoveStyle(attr.m_bottom);
12585 return true;
12586}
12587
12588// Collects the attributes that are common to a range of content, building up a note of
12589// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12590void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
12591{
12592 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12593 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12594 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12595 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12596}
12597
12598// Set style of all borders
bec80f4f 12599void wxTextAttrBorders::SetStyle(int style)
24777478
JS
12600{
12601 m_left.SetStyle(style);
12602 m_right.SetStyle(style);
12603 m_top.SetStyle(style);
12604 m_bottom.SetStyle(style);
12605}
12606
12607// Set colour of all borders
bec80f4f 12608void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
12609{
12610 m_left.SetColour(colour);
12611 m_right.SetColour(colour);
12612 m_top.SetColour(colour);
12613 m_bottom.SetColour(colour);
12614}
12615
bec80f4f 12616void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
12617{
12618 m_left.SetColour(colour);
12619 m_right.SetColour(colour);
12620 m_top.SetColour(colour);
12621 m_bottom.SetColour(colour);
12622}
12623
12624// Set width of all borders
bec80f4f 12625void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
12626{
12627 m_left.SetWidth(width);
12628 m_right.SetWidth(width);
12629 m_top.SetWidth(width);
12630 m_bottom.SetWidth(width);
12631}
12632
12633// Partial equality test
32423dd8 12634bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
24777478 12635{
32423dd8
JS
12636 if (!weakTest && !IsValid() && dim.IsValid())
12637 return false;
12638
603f702b 12639 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
12640 return false;
12641 else
12642 return true;
12643}
12644
12645bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12646{
603f702b 12647 if (dim.IsValid())
24777478
JS
12648 {
12649 if (!(compareWith && dim == (*compareWith)))
12650 (*this) = dim;
12651 }
12652
12653 return true;
12654}
12655
12656// Collects the attributes that are common to a range of content, building up a note of
12657// which attributes are absent in some objects and which clash in some objects.
12658void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12659{
603f702b 12660 if (attr.IsValid())
24777478 12661 {
603f702b 12662 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 12663 {
603f702b 12664 if (IsValid())
24777478
JS
12665 {
12666 if (!((*this) == attr))
12667 {
603f702b
JS
12668 clashingAttr.SetValid(true);
12669 SetValid(false);
24777478
JS
12670 }
12671 }
12672 else
12673 (*this) = attr;
12674 }
12675 }
12676 else
603f702b 12677 absentAttr.SetValid(true);
24777478
JS
12678}
12679
8995db52
JS
12680wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12681{
12682 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12683}
12684
12685wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12686{
12687 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12688}
12689
bec80f4f
JS
12690int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12691{
12692 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12693}
12694
12695int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12696{
12697 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12698}
12699
12700int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12701{
12702 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12703 return ConvertTenthsMMToPixels(dim.GetValue());
12704 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12705 return dim.GetValue();
12706 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12707 {
12708 wxASSERT(m_parentSize != wxDefaultSize);
12709 if (direction == wxHORIZONTAL)
12710 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12711 else
12712 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12713 }
12714 else
12715 {
12716 wxASSERT(false);
12717 return 0;
12718 }
12719}
12720
12721int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12722{
12723 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12724 return dim.GetValue();
12725 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12726 return ConvertPixelsToTenthsMM(dim.GetValue());
12727 else
12728 {
12729 wxASSERT(false);
12730 return 0;
12731 }
12732}
12733
24777478 12734// Partial equality test
32423dd8 12735bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
24777478 12736{
32423dd8 12737 if (!m_left.EqPartial(dims.m_left, weakTest))
24777478
JS
12738 return false;
12739
32423dd8 12740 if (!m_right.EqPartial(dims.m_right, weakTest))
24777478
JS
12741 return false;
12742
32423dd8 12743 if (!m_top.EqPartial(dims.m_top, weakTest))
24777478
JS
12744 return false;
12745
32423dd8 12746 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
24777478
JS
12747 return false;
12748
12749 return true;
12750}
12751
12752// Apply border to 'this', but not if the same as compareWith
bec80f4f 12753bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
12754{
12755 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12756 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12757 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12758 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12759
12760 return true;
12761}
12762
12763// Remove specified attributes from this object
bec80f4f 12764bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 12765{
603f702b 12766 if (attr.m_left.IsValid())
24777478 12767 m_left.Reset();
603f702b 12768 if (attr.m_right.IsValid())
24777478 12769 m_right.Reset();
603f702b 12770 if (attr.m_top.IsValid())
24777478 12771 m_top.Reset();
603f702b 12772 if (attr.m_bottom.IsValid())
24777478
JS
12773 m_bottom.Reset();
12774
12775 return true;
12776}
12777
12778// Collects the attributes that are common to a range of content, building up a note of
12779// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12780void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
12781{
12782 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12783 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12784 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12785 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12786}
12787
603f702b 12788// Partial equality test
32423dd8 12789bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
603f702b 12790{
32423dd8 12791 if (!m_width.EqPartial(size.m_width, weakTest))
603f702b
JS
12792 return false;
12793
32423dd8 12794 if (!m_height.EqPartial(size.m_height, weakTest))
603f702b
JS
12795 return false;
12796
12797 return true;
12798}
12799
12800// Apply border to 'this', but not if the same as compareWith
12801bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12802{
12803 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12804 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12805
12806 return true;
12807}
12808
12809// Remove specified attributes from this object
12810bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12811{
12812 if (attr.m_width.IsValid())
12813 m_width.Reset();
12814 if (attr.m_height.IsValid())
12815 m_height.Reset();
12816
12817 return true;
12818}
12819
12820// Collects the attributes that are common to a range of content, building up a note of
12821// which attributes are absent in some objects and which clash in some objects.
12822void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12823{
12824 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12825 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12826}
12827
24777478
JS
12828// Collects the attributes that are common to a range of content, building up a note of
12829// which attributes are absent in some objects and which clash in some objects.
12830void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12831{
12832 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12833 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12834
12835 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12836
c4168888
JS
12837 // If different font size units are being used, this is a clash.
12838 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
24777478 12839 {
c4168888
JS
12840 currentStyle.SetFontSize(0);
12841 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12842 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12843 }
12844 else
12845 {
12846 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
340ef5c5 12847 {
c4168888 12848 if (currentStyle.HasFontPointSize())
24777478 12849 {
c4168888 12850 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478 12851 {
c4168888
JS
12852 // Clash of attr - mark as such
12853 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12854 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
12855 }
12856 }
c4168888
JS
12857 else
12858 currentStyle.SetFontSize(attr.GetFontSize());
12859 }
12860 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
12861 {
12862 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12863 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
12864 }
12865
c4168888 12866 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
24777478 12867 {
c4168888 12868 if (currentStyle.HasFontPixelSize())
24777478 12869 {
c4168888 12870 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478
JS
12871 {
12872 // Clash of attr - mark as such
c4168888
JS
12873 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12874 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
24777478
JS
12875 }
12876 }
12877 else
c4168888 12878 currentStyle.SetFontPixelSize(attr.GetFontSize());
24777478 12879 }
c4168888
JS
12880 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
12881 {
12882 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12883 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12884 }
12885 }
24777478 12886
c4168888
JS
12887 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12888 {
12889 if (currentStyle.HasFontItalic())
24777478 12890 {
c4168888 12891 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
24777478 12892 {
c4168888
JS
12893 // Clash of attr - mark as such
12894 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12895 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
24777478 12896 }
24777478 12897 }
c4168888
JS
12898 else
12899 currentStyle.SetFontStyle(attr.GetFontStyle());
12900 }
12901 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
12902 {
12903 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12904 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12905 }
24777478 12906
c4168888
JS
12907 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12908 {
12909 if (currentStyle.HasFontFamily())
24777478 12910 {
c4168888 12911 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
24777478 12912 {
c4168888
JS
12913 // Clash of attr - mark as such
12914 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12915 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
24777478 12916 }
24777478 12917 }
c4168888
JS
12918 else
12919 currentStyle.SetFontFamily(attr.GetFontFamily());
12920 }
12921 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
12922 {
12923 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12924 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12925 }
24777478 12926
c4168888
JS
12927 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12928 {
12929 if (currentStyle.HasFontWeight())
24777478 12930 {
c4168888 12931 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
24777478 12932 {
c4168888
JS
12933 // Clash of attr - mark as such
12934 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12935 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12936 }
12937 }
12938 else
12939 currentStyle.SetFontWeight(attr.GetFontWeight());
12940 }
12941 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
12942 {
12943 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12944 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12945 }
24777478 12946
c4168888
JS
12947 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12948 {
12949 if (currentStyle.HasFontFaceName())
12950 {
12951 wxString faceName1(currentStyle.GetFontFaceName());
12952 wxString faceName2(attr.GetFontFaceName());
12953
12954 if (faceName1 != faceName2)
12955 {
12956 // Clash of attr - mark as such
12957 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
12958 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
24777478 12959 }
24777478 12960 }
c4168888
JS
12961 else
12962 currentStyle.SetFontFaceName(attr.GetFontFaceName());
12963 }
12964 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
12965 {
12966 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
12967 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
12968 }
24777478 12969
c4168888
JS
12970 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
12971 {
12972 if (currentStyle.HasFontUnderlined())
24777478 12973 {
c4168888 12974 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
24777478 12975 {
c4168888
JS
12976 // Clash of attr - mark as such
12977 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12978 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
24777478 12979 }
24777478 12980 }
c4168888
JS
12981 else
12982 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
12983 }
12984 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
12985 {
12986 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12987 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12988 }
32423dd8 12989
c4168888
JS
12990 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
12991 {
12992 if (currentStyle.HasFontStrikethrough())
32423dd8 12993 {
c4168888 12994 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
32423dd8 12995 {
c4168888
JS
12996 // Clash of attr - mark as such
12997 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12998 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
32423dd8 12999 }
32423dd8 13000 }
c4168888
JS
13001 else
13002 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13003 }
13004 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13005 {
13006 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13007 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
24777478
JS
13008 }
13009
13010 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13011 {
13012 if (currentStyle.HasTextColour())
13013 {
13014 if (currentStyle.GetTextColour() != attr.GetTextColour())
13015 {
13016 // Clash of attr - mark as such
13017 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13018 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13019 }
13020 }
13021 else
13022 currentStyle.SetTextColour(attr.GetTextColour());
13023 }
c4168888
JS
13024 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13025 {
13026 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13027 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13028 }
24777478
JS
13029
13030 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13031 {
13032 if (currentStyle.HasBackgroundColour())
13033 {
13034 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13035 {
13036 // Clash of attr - mark as such
13037 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13038 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13039 }
13040 }
13041 else
13042 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13043 }
c4168888
JS
13044 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13045 {
13046 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13047 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13048 }
24777478
JS
13049
13050 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13051 {
13052 if (currentStyle.HasAlignment())
13053 {
13054 if (currentStyle.GetAlignment() != attr.GetAlignment())
13055 {
13056 // Clash of attr - mark as such
13057 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13058 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13059 }
13060 }
13061 else
13062 currentStyle.SetAlignment(attr.GetAlignment());
13063 }
c4168888
JS
13064 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13065 {
13066 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13067 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13068 }
24777478
JS
13069
13070 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13071 {
13072 if (currentStyle.HasTabs())
13073 {
13074 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13075 {
13076 // Clash of attr - mark as such
13077 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13078 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13079 }
13080 }
13081 else
13082 currentStyle.SetTabs(attr.GetTabs());
13083 }
c4168888
JS
13084 else if (!attr.HasTabs() && currentStyle.HasTabs())
13085 {
13086 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13087 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13088 }
24777478
JS
13089
13090 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13091 {
13092 if (currentStyle.HasLeftIndent())
13093 {
13094 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13095 {
13096 // Clash of attr - mark as such
13097 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13098 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13099 }
13100 }
13101 else
13102 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13103 }
c4168888
JS
13104 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13105 {
13106 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13107 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13108 }
24777478
JS
13109
13110 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13111 {
13112 if (currentStyle.HasRightIndent())
13113 {
13114 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13115 {
13116 // Clash of attr - mark as such
13117 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13118 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13119 }
13120 }
13121 else
13122 currentStyle.SetRightIndent(attr.GetRightIndent());
13123 }
c4168888
JS
13124 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13125 {
13126 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13127 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13128 }
24777478
JS
13129
13130 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13131 {
13132 if (currentStyle.HasParagraphSpacingAfter())
13133 {
13134 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13135 {
13136 // Clash of attr - mark as such
13137 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13138 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13139 }
13140 }
13141 else
13142 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13143 }
c4168888
JS
13144 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13145 {
13146 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13147 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13148 }
24777478
JS
13149
13150 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13151 {
13152 if (currentStyle.HasParagraphSpacingBefore())
13153 {
13154 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13155 {
13156 // Clash of attr - mark as such
13157 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13158 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13159 }
13160 }
13161 else
13162 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13163 }
c4168888
JS
13164 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13165 {
13166 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13167 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13168 }
24777478
JS
13169
13170 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13171 {
13172 if (currentStyle.HasLineSpacing())
13173 {
13174 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13175 {
13176 // Clash of attr - mark as such
13177 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13178 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13179 }
13180 }
13181 else
13182 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13183 }
c4168888
JS
13184 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13185 {
13186 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13187 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13188 }
24777478
JS
13189
13190 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13191 {
13192 if (currentStyle.HasCharacterStyleName())
13193 {
13194 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13195 {
13196 // Clash of attr - mark as such
13197 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13198 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13199 }
13200 }
13201 else
13202 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13203 }
c4168888
JS
13204 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13205 {
13206 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13207 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13208 }
24777478
JS
13209
13210 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13211 {
13212 if (currentStyle.HasParagraphStyleName())
13213 {
13214 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13215 {
13216 // Clash of attr - mark as such
13217 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13218 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13219 }
13220 }
13221 else
13222 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13223 }
c4168888
JS
13224 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13225 {
13226 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13227 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13228 }
24777478
JS
13229
13230 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13231 {
13232 if (currentStyle.HasListStyleName())
13233 {
13234 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13235 {
13236 // Clash of attr - mark as such
13237 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13238 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13239 }
13240 }
13241 else
13242 currentStyle.SetListStyleName(attr.GetListStyleName());
13243 }
c4168888
JS
13244 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13245 {
13246 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13247 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13248 }
24777478
JS
13249
13250 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13251 {
13252 if (currentStyle.HasBulletStyle())
13253 {
13254 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13255 {
13256 // Clash of attr - mark as such
13257 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13258 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13259 }
13260 }
13261 else
13262 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13263 }
c4168888
JS
13264 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13265 {
13266 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13267 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13268 }
24777478
JS
13269
13270 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13271 {
13272 if (currentStyle.HasBulletNumber())
13273 {
13274 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13275 {
13276 // Clash of attr - mark as such
13277 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13278 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13279 }
13280 }
13281 else
13282 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13283 }
c4168888
JS
13284 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13285 {
13286 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13287 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13288 }
24777478
JS
13289
13290 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13291 {
13292 if (currentStyle.HasBulletText())
13293 {
13294 if (currentStyle.GetBulletText() != attr.GetBulletText())
13295 {
13296 // Clash of attr - mark as such
13297 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13298 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13299 }
13300 }
13301 else
13302 {
13303 currentStyle.SetBulletText(attr.GetBulletText());
13304 currentStyle.SetBulletFont(attr.GetBulletFont());
13305 }
13306 }
c4168888
JS
13307 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13308 {
13309 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13310 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13311 }
24777478
JS
13312
13313 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13314 {
13315 if (currentStyle.HasBulletName())
13316 {
13317 if (currentStyle.GetBulletName() != attr.GetBulletName())
13318 {
13319 // Clash of attr - mark as such
13320 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13321 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13322 }
13323 }
13324 else
13325 {
13326 currentStyle.SetBulletName(attr.GetBulletName());
13327 }
13328 }
c4168888
JS
13329 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13330 {
13331 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13332 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13333 }
24777478
JS
13334
13335 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13336 {
13337 if (currentStyle.HasURL())
13338 {
13339 if (currentStyle.GetURL() != attr.GetURL())
13340 {
13341 // Clash of attr - mark as such
13342 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13343 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13344 }
13345 }
13346 else
13347 {
13348 currentStyle.SetURL(attr.GetURL());
13349 }
13350 }
c4168888
JS
13351 else if (!attr.HasURL() && currentStyle.HasURL())
13352 {
13353 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13354 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13355 }
24777478
JS
13356
13357 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13358 {
13359 if (currentStyle.HasTextEffects())
13360 {
13361 // We need to find the bits in the new attr that are different:
13362 // just look at those bits that are specified by the new attr.
13363
13364 // We need to remove the bits and flags that are not common between current attr
13365 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13366 // previous styles.
13367
13368 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13369 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13370
13371 if (currentRelevantTextEffects != newRelevantTextEffects)
13372 {
13373 // Find the text effects that were different, using XOR
13374 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13375
13376 // Clash of attr - mark as such
13377 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13378 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13379 }
13380 }
13381 else
13382 {
13383 currentStyle.SetTextEffects(attr.GetTextEffects());
13384 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13385 }
13386
13387 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13388 // that we've looked at so far
13389 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13390 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13391
13392 if (currentStyle.GetTextEffectFlags() == 0)
13393 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13394 }
c4168888
JS
13395 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13396 {
13397 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13398 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13399 }
24777478
JS
13400
13401 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13402 {
13403 if (currentStyle.HasOutlineLevel())
13404 {
13405 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13406 {
13407 // Clash of attr - mark as such
13408 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13409 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13410 }
13411 }
13412 else
13413 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13414 }
c4168888
JS
13415 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13416 {
13417 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13418 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13419 }
24777478
JS
13420}
13421
bec80f4f
JS
13422WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13423
f7667b84
JS
13424// JACS 2013-01-27
13425WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13426
bec80f4f
JS
13427IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13428
13429bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13430{
13431 if (m_properties.GetCount() != props.GetCount())
13432 return false;
13433
13434 size_t i;
13435 for (i = 0; i < m_properties.GetCount(); i++)
13436 {
13437 const wxVariant& var1 = m_properties[i];
13438 int idx = props.Find(var1.GetName());
13439 if (idx == -1)
13440 return false;
13441 const wxVariant& var2 = props.m_properties[idx];
13442 if (!(var1 == var2))
13443 return false;
13444 }
13445
13446 return true;
13447}
13448
13449wxArrayString wxRichTextProperties::GetPropertyNames() const
13450{
13451 wxArrayString arr;
13452 size_t i;
13453 for (i = 0; i < m_properties.GetCount(); i++)
13454 {
13455 arr.Add(m_properties[i].GetName());
13456 }
13457 return arr;
13458}
13459
13460int wxRichTextProperties::Find(const wxString& name) const
13461{
13462 size_t i;
13463 for (i = 0; i < m_properties.GetCount(); i++)
13464 {
13465 if (m_properties[i].GetName() == name)
13466 return (int) i;
13467 }
13468 return -1;
13469}
13470
590a0f8b
JS
13471bool wxRichTextProperties::Remove(const wxString& name)
13472{
13473 int idx = Find(name);
13474 if (idx != -1)
13475 {
13476 m_properties.RemoveAt(idx);
13477 return true;
13478 }
13479 else
13480 return false;
13481}
13482
bec80f4f
JS
13483wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13484{
13485 int idx = Find(name);
13486 if (idx == wxNOT_FOUND)
13487 SetProperty(name, wxString());
13488 idx = Find(name);
13489 if (idx != wxNOT_FOUND)
13490 {
13491 return & (*this)[idx];
13492 }
13493 else
13494 return NULL;
13495}
13496
13497const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13498{
13499 static const wxVariant nullVariant;
13500 int idx = Find(name);
13501 if (idx != -1)
13502 return m_properties[idx];
13503 else
13504 return nullVariant;
13505}
13506
13507wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13508{
13509 return GetProperty(name).GetString();
13510}
13511
13512long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13513{
13514 return GetProperty(name).GetLong();
13515}
13516
13517bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13518{
13519 return GetProperty(name).GetBool();
13520}
13521
13522double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13523{
13524 return GetProperty(name).GetDouble();
13525}
13526
13527void wxRichTextProperties::SetProperty(const wxVariant& variant)
13528{
13529 wxASSERT(!variant.GetName().IsEmpty());
13530
13531 int idx = Find(variant.GetName());
13532
13533 if (idx == -1)
13534 m_properties.Add(variant);
13535 else
13536 m_properties[idx] = variant;
13537}
13538
13539void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13540{
13541 int idx = Find(name);
13542 wxVariant var(variant);
13543 var.SetName(name);
13544
13545 if (idx == -1)
13546 m_properties.Add(var);
13547 else
13548 m_properties[idx] = var;
13549}
13550
13551void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13552{
13553 SetProperty(name, wxVariant(value, name));
13554}
13555
13556void wxRichTextProperties::SetProperty(const wxString& name, long value)
13557{
13558 SetProperty(name, wxVariant(value, name));
13559}
13560
13561void wxRichTextProperties::SetProperty(const wxString& name, double value)
13562{
13563 SetProperty(name, wxVariant(value, name));
13564}
13565
13566void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13567{
13568 SetProperty(name, wxVariant(value, name));
13569}
24777478 13570
590a0f8b
JS
13571void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13572{
13573 size_t i;
13574 for (i = 0; i < properties.GetCount(); i++)
13575 {
13576 wxString name = properties.GetProperties()[i].GetName();
13577 if (HasProperty(name))
13578 Remove(name);
13579 }
13580}
13581
13582void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13583{
13584 size_t i;
13585 for (i = 0; i < properties.GetCount(); i++)
13586 {
13587 SetProperty(properties.GetProperties()[i]);
13588 }
13589}
13590
603f702b
JS
13591wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13592{
13593 if (m_address.GetCount() == 0)
13594 return topLevelContainer;
13595
13596 wxRichTextCompositeObject* p = topLevelContainer;
13597 size_t i = 0;
13598 while (p && i < m_address.GetCount())
13599 {
13600 int pos = m_address[i];
13601 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13602 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13603 return NULL;
13604
13605 wxRichTextObject* p1 = p->GetChild(pos);
13606 if (i == (m_address.GetCount()-1))
13607 return p1;
13608
13609 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13610 i ++;
13611 }
13612 return NULL;
13613}
13614
13615bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13616{
13617 m_address.Clear();
13618
13619 if (topLevelContainer == obj)
13620 return true;
13621
13622 wxRichTextObject* o = obj;
13623 while (o)
13624 {
13625 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13626 if (!p)
13627 return false;
13628
13629 int pos = p->GetChildren().IndexOf(o);
13630 if (pos == -1)
13631 return false;
13632
13633 m_address.Insert(pos, 0);
13634
13635 if (p == topLevelContainer)
13636 return true;
13637
13638 o = p;
13639 }
13640 return false;
13641}
13642
13643// Equality test
13644bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13645{
13646 if (m_container != sel.m_container)
13647 return false;
13648 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13649 return false;
13650 size_t i;
13651 for (i = 0; i < m_ranges.GetCount(); i++)
13652 if (!(m_ranges[i] == sel.m_ranges[i]))
13653 return false;
13654 return true;
13655}
13656
13657// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13658// or none at the level of the object's container.
13659wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13660{
13661 if (IsValid())
13662 {
13663 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13664
13665 if (container == m_container)
13666 return m_ranges;
13667
13668 container = obj->GetContainer();
13669 while (container)
13670 {
13671 if (container->GetParent())
13672 {
13673 // If we found that our object's container is within the range of
13674 // a selection higher up, then assume the whole original object
13675 // is also selected.
13676 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13677 if (parentContainer == m_container)
13678 {
13679 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13680 {
13681 wxRichTextRangeArray ranges;
13682 ranges.Add(obj->GetRange());
13683 return ranges;
13684 }
13685 }
13686
13687 container = parentContainer;
13688 }
13689 else
13690 {
13691 container = NULL;
13692 break;
13693 }
13694 }
13695 }
13696 return wxRichTextRangeArray();
13697}
13698
13699// Is the given position within the selection?
13700bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13701{
13702 if (!IsValid())
13703 return false;
13704 else
13705 {
13706 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13707 return WithinSelection(pos, selectionRanges);
13708 }
13709}
13710
13711// Is the given position within the selection range?
13712bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13713{
13714 size_t i;
13715 for (i = 0; i < ranges.GetCount(); i++)
13716 {
13717 const wxRichTextRange& range = ranges[i];
13718 if (pos >= range.GetStart() && pos <= range.GetEnd())
13719 return true;
13720 }
13721 return false;
13722}
13723
13724// Is the given range completely within the selection range?
13725bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13726{
13727 size_t i;
13728 for (i = 0; i < ranges.GetCount(); i++)
13729 {
13730 const wxRichTextRange& eachRange = ranges[i];
13731 if (range.IsWithin(eachRange))
13732 return true;
13733 }
13734 return false;
13735}
13736
8db2e3ef
JS
13737IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13738IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13739
f7667b84
JS
13740wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
13741{
13742 Init();
13743 m_buffer = buffer;
13744 if (m_buffer && m_buffer->GetRichTextCtrl())
13745 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13746}
13747
8db2e3ef
JS
13748bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13749{
f7667b84
JS
13750 if (!GetVirtualAttributesEnabled())
13751 return false;
13752
8db2e3ef
JS
13753 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13754 while (node)
13755 {
13756 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13757 if (handler->HasVirtualAttributes(obj))
13758 return true;
13759
13760 node = node->GetNext();
13761 }
13762 return false;
13763}
13764
13765wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13766{
13767 wxRichTextAttr attr;
f7667b84
JS
13768 if (!GetVirtualAttributesEnabled())
13769 return attr;
13770
8db2e3ef
JS
13771 // We apply all handlers, so we can may combine several different attributes
13772 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13773 while (node)
13774 {
13775 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13776 if (handler->HasVirtualAttributes(obj))
13777 {
13778 bool success = handler->GetVirtualAttributes(attr, obj);
13779 wxASSERT(success);
aa8f57f4 13780 wxUnusedVar(success);
8db2e3ef
JS
13781 }
13782
13783 node = node->GetNext();
13784 }
13785 return attr;
13786}
13787
13788bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13789{
f7667b84
JS
13790 if (!GetVirtualAttributesEnabled())
13791 return false;
13792
8db2e3ef
JS
13793 if (HasVirtualAttributes(obj))
13794 {
13795 wxRichTextAttr a(GetVirtualAttributes(obj));
13796 attr.Apply(a);
13797 return true;
13798 }
13799 else
13800 return false;
13801}
13802
f7667b84
JS
13803int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
13804{
13805 if (!GetVirtualAttributesEnabled())
13806 return 0;
13807
13808 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13809 while (node)
13810 {
13811 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13812 int count = handler->GetVirtualSubobjectAttributesCount(obj);
13813 if (count > 0)
13814 return count;
13815
13816 node = node->GetNext();
13817 }
13818 return 0;
13819}
13820
13821int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
13822{
13823 if (!GetVirtualAttributesEnabled())
13824 return 0;
13825
13826 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13827 while (node)
13828 {
13829 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13830 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
13831 return positions.GetCount();
13832
13833 node = node->GetNext();
13834 }
13835 return 0;
13836}
13837
13838bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
13839{
13840 if (!GetVirtualAttributesEnabled())
13841 return false;
13842
13843 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13844 while (node)
13845 {
13846 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13847 if (handler->HasVirtualText(obj))
13848 return true;
13849
13850 node = node->GetNext();
13851 }
13852 return false;
13853}
13854
13855bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
13856{
13857 if (!GetVirtualAttributesEnabled())
13858 return false;
13859
13860 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13861 while (node)
13862 {
13863 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13864 if (handler->GetVirtualText(obj, text))
13865 return true;
13866
13867 node = node->GetNext();
13868 }
13869 return false;
13870}
13871
8db2e3ef
JS
13872/// Adds a handler to the end
13873void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13874{
13875 sm_drawingHandlers.Append(handler);
13876}
13877
13878/// Inserts a handler at the front
13879void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13880{
13881 sm_drawingHandlers.Insert( handler );
13882}
13883
13884/// Removes a handler
13885bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13886{
13887 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13888 if (handler)
13889 {
13890 sm_drawingHandlers.DeleteObject(handler);
13891 delete handler;
13892 return true;
13893 }
13894 else
13895 return false;
13896}
13897
13898wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13899{
13900 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13901 while (node)
13902 {
13903 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13904 if (handler->GetName().Lower() == name.Lower()) return handler;
13905
13906 node = node->GetNext();
13907 }
13908 return NULL;
13909}
13910
13911void wxRichTextBuffer::CleanUpDrawingHandlers()
13912{
13913 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13914 while (node)
13915 {
13916 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13917 wxList::compatibility_iterator next = node->GetNext();
13918 delete handler;
13919 node = next;
13920 }
13921
13922 sm_drawingHandlers.Clear();
13923}
603f702b 13924
7c9fdebe
JS
13925void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13926{
13927 sm_fieldTypes[fieldType->GetName()] = fieldType;
13928}
13929
13930bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13931{
13932 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13933 if (it == sm_fieldTypes.end())
13934 return false;
13935 else
13936 {
13937 wxRichTextFieldType* fieldType = it->second;
13938 sm_fieldTypes.erase(it);
13939 delete fieldType;
13940 return true;
13941 }
13942}
13943
13944wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13945{
13946 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13947 if (it == sm_fieldTypes.end())
13948 return NULL;
13949 else
13950 return it->second;
13951}
13952
13953void wxRichTextBuffer::CleanUpFieldTypes()
13954{
13955 wxRichTextFieldTypeHashMap::iterator it;
13956 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
13957 {
13958 wxRichTextFieldType* fieldType = it->second;
13959 delete fieldType;
13960 }
13961
13962 sm_fieldTypes.clear();
13963}
13964
5d7836c4
JS
13965#endif
13966 // wxUSE_RICHTEXT