]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
We need to switch off UI elements before they are laid out. Do this via static functions.
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
CommitLineData
5d7836c4 1/////////////////////////////////////////////////////////////////////////////
61399247 2// Name: src/richtext/richtextbuffer.cpp
5d7836c4
JS
3// Purpose: Buffer for wxRichTextCtrl
4// Author: Julian Smart
7fe8059f 5// Modified by:
5d7836c4 6// Created: 2005-09-30
7fe8059f 7// RCS-ID: $Id$
5d7836c4
JS
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
2be72ac2 11
5d7836c4
JS
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
61399247 16 #pragma hdrstop
5d7836c4
JS
17#endif
18
b01ca8b6
JS
19#if wxUSE_RICHTEXT
20
21#include "wx/richtext/richtextbuffer.h"
22
5d7836c4 23#ifndef WX_PRECOMP
61399247
WS
24 #include "wx/dc.h"
25 #include "wx/intl.h"
7947a48a 26 #include "wx/log.h"
28f92d74 27 #include "wx/dataobj.h"
02761f6c 28 #include "wx/module.h"
5d7836c4
JS
29#endif
30
0ec6da02 31#include "wx/settings.h"
5d7836c4
JS
32#include "wx/filename.h"
33#include "wx/clipbrd.h"
34#include "wx/wfstream.h"
5d7836c4
JS
35#include "wx/mstream.h"
36#include "wx/sstream.h"
0ca07313 37#include "wx/textfile.h"
44cc96a8 38#include "wx/hashmap.h"
cdaed652 39#include "wx/dynarray.h"
5d7836c4 40
5d7836c4
JS
41#include "wx/richtext/richtextctrl.h"
42#include "wx/richtext/richtextstyles.h"
cdaed652 43#include "wx/richtext/richtextimagedlg.h"
603f702b 44#include "wx/richtext/richtextsizepage.h"
1aca9fcd 45#include "wx/richtext/richtextxml.h"
5d7836c4
JS
46
47#include "wx/listimpl.cpp"
bec80f4f 48#include "wx/arrimpl.cpp"
5d7836c4 49
412e0d47
DS
50WX_DEFINE_LIST(wxRichTextObjectList)
51WX_DEFINE_LIST(wxRichTextLineList)
5d7836c4 52
ea160b2e
JS
53// Switch off if the platform doesn't like it for some reason
54#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55
31778480
JS
56// Use GetPartialTextExtents for platforms that support it natively
57#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58
ff76711f
JS
59const wxChar wxRichTextLineBreakChar = (wxChar) 29;
60
cdaed652
VZ
61// Helper classes for floating layout
62struct wxRichTextFloatRectMap
63{
64 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
65 {
66 startY = sY;
67 endY = eY;
68 width = w;
69 anchor = obj;
70 }
71
72 int startY, endY;
73 int width;
74 wxRichTextObject* anchor;
75};
76
77WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
78
79int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
80{
81 return r1->startY - r2->startY;
82}
83
84class wxRichTextFloatCollector
85{
86public:
603f702b 87 wxRichTextFloatCollector(const wxRect& availableRect);
cdaed652
VZ
88 ~wxRichTextFloatCollector();
89
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph* para);
92 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
93
94 // Return the last paragraph we collected
95 wxRichTextParagraph* LastParagraph();
96
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect GetAvailableRect(int startY, int endY);
100
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction, int start, int height) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
104
105 // Find the last y position
106 int GetLastRectBottom();
107
108 // Draw the floats inside a rect
8db2e3ef 109 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652
VZ
110
111 // HitTest the floats
8db2e3ef 112 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
603f702b
JS
113
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
116
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
cdaed652 119
07d4142f
JS
120 // Delete a float
121 bool DeleteFloat(wxRichTextObject* obj);
122
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject* obj);
125
126 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
127
cdaed652
VZ
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
129
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
ce00f59b 131
cdaed652
VZ
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
133
8db2e3ef 134 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652 135
8db2e3ef 136 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
ce00f59b 137
cdaed652
VZ
138private:
139 wxRichTextFloatRectMapArray m_left;
140 wxRichTextFloatRectMapArray m_right;
603f702b
JS
141 //int m_width;
142 wxRect m_availableRect;
cdaed652
VZ
143 wxRichTextParagraph* m_para;
144};
145
07d4142f
JS
146// Delete a float
147bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
148{
149 size_t i;
150 for (i = 0; i < m_left.GetCount(); i++)
151 {
152 if (m_left[i]->anchor == obj)
153 {
154 m_left.RemoveAt(i);
155 return true;
156 }
157 }
158 for (i = 0; i < m_right.GetCount(); i++)
159 {
160 if (m_right[i]->anchor == obj)
161 {
162 m_right.RemoveAt(i);
163 return true;
164 }
165 }
166 return false;
167}
168
169// Do we have this float already?
170bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
171{
172 size_t i;
173 for (i = 0; i < m_left.GetCount(); i++)
174 {
175 if (m_left[i]->anchor == obj)
176 {
177 return true;
178 }
179 }
180 for (i = 0; i < m_right.GetCount(); i++)
181 {
182 if (m_right[i]->anchor == obj)
183 {
184 return true;
185 }
186 }
187 return false;
188}
189
603f702b
JS
190// Get floating objects
191bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
192{
193 size_t i;
194 for (i = 0; i < m_left.GetCount(); i++)
195 objects.Append(m_left[i]->anchor);
196 for (i = 0; i < m_right.GetCount(); i++)
197 objects.Append(m_right[i]->anchor);
198 return true;
199}
200
201
cdaed652
VZ
202/*
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
207 */
208int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
209{
210 int end = array.GetCount() - 1;
211 int start = 0;
212 int ret = 0;
213
214 wxASSERT(end >= 0);
215
216 while (true)
217 {
218 if (start > end)
219 {
220 break;
221 }
222
223 int mid = (start + end) / 2;
224 if (array[mid]->startY <= point && array[mid]->endY >= point)
225 return mid;
226 else if (array[mid]->startY > point)
227 {
228 end = mid - 1;
229 ret = mid;
230 }
231 else if (array[mid]->endY < point)
232 {
233 start = mid + 1;
234 ret = start;
235 }
236 }
237
238 return ret;
239}
240
241int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
242{
243 int ret = 0;
244 int len = array.GetCount();
245
246 wxASSERT(index >= 0 && index < len);
247
248 if (array[index]->startY < startY && array[index]->endY > startY)
249 ret = ret < array[index]->width ? array[index]->width : ret;
250 while (index < len && array[index]->startY <= endY)
251 {
252 ret = ret < array[index]->width ? array[index]->width : ret;
253 index++;
254 }
255
256 return ret;
257}
258
603f702b 259wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
cdaed652 260{
603f702b 261 m_availableRect = rect;
cdaed652
VZ
262 m_para = NULL;
263}
264
265void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
266{
267 int len = array.GetCount();
268 for (int i = 0; i < len; i++)
269 delete array[i];
270}
271
272wxRichTextFloatCollector::~wxRichTextFloatCollector()
273{
274 FreeFloatRectMapArray(m_left);
275 FreeFloatRectMapArray(m_right);
276}
277
278int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
279{
280 if (array.GetCount() == 0)
281 return start;
282
603f702b 283 int i = SearchAdjacentRect(array, start);
cdaed652 284 int last = start;
603f702b 285 while (i < (int) array.GetCount())
cdaed652
VZ
286 {
287 if (array[i]->startY - last >= height)
288 return last + 1;
289 last = array[i]->endY;
290 i++;
291 }
292
293 return last + 1;
294}
295
296int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
297{
24777478 298 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
cdaed652 299 return GetFitPosition(m_left, start, height);
24777478 300 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
cdaed652
VZ
301 return GetFitPosition(m_right, start, height);
302 else
303 {
304 wxASSERT("Never should be here");
305 return start;
306 }
307}
308
603f702b
JS
309// Adds a floating image to the float collector.
310// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
cdaed652
VZ
311void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
312{
603f702b 313 int direction = floating->GetFloatDirection();
24777478 314
603f702b
JS
315 wxPoint pos = floating->GetPosition();
316 wxSize size = floating->GetCachedSize();
317 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
318 switch (direction)
319 {
320 case wxTEXT_BOX_ATTR_FLOAT_NONE:
321 delete map;
322 break;
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
326 m_left.Add(map);
327 break;
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
330 m_right.Add(map);
331 break;
332 default:
333 delete map;
334 wxASSERT("Unrecognised float attribute.");
335 }
cdaed652 336
603f702b 337 m_para = para;
cdaed652
VZ
338}
339
340void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
341{
342 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
343 while (node)
344 {
345 wxRichTextObject* floating = node->GetData();
ce00f59b 346
cdaed652
VZ
347 if (floating->IsFloating())
348 {
bec80f4f 349 CollectFloat(para, floating);
cdaed652 350 }
ce00f59b 351
cdaed652
VZ
352 node = node->GetNext();
353 }
354
355 m_para = para;
356}
357
358wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
359{
360 return m_para;
361}
362
363wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
364{
365 int widthLeft = 0, widthRight = 0;
366 if (m_left.GetCount() != 0)
367 {
603f702b
JS
368 int i = SearchAdjacentRect(m_left, startY);
369 if (i < (int) m_left.GetCount())
cdaed652
VZ
370 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
371 }
372 if (m_right.GetCount() != 0)
373 {
603f702b
JS
374 int j = SearchAdjacentRect(m_right, startY);
375 if (j < (int) m_right.GetCount())
cdaed652
VZ
376 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
377 }
378
603f702b
JS
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
cdaed652
VZ
383}
384
385int wxRichTextFloatCollector::GetLastRectBottom()
386{
387 int ret = 0;
388 int len = m_left.GetCount();
389 if (len) {
390 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
391 }
392 len = m_right.GetCount();
393 if (len) {
394 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
395 }
396
397 return ret;
398}
399
8db2e3ef 400void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
401{
402 int start = rect.y;
403 int end = rect.y + rect.height;
603f702b 404 int i, j;
cdaed652 405 i = SearchAdjacentRect(array, start);
603f702b 406 if (i < 0 || i >= (int) array.GetCount())
cdaed652
VZ
407 return;
408 j = SearchAdjacentRect(array, end);
603f702b 409 if (j < 0 || j >= (int) array.GetCount())
cdaed652
VZ
410 j = array.GetCount() - 1;
411 while (i <= j)
412 {
413 wxRichTextObject* obj = array[i]->anchor;
414 wxRichTextRange r = obj->GetRange();
8db2e3ef 415 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
cdaed652
VZ
416 i++;
417 }
418}
ecb5fbf1 419
8db2e3ef 420void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
421{
422 if (m_left.GetCount() > 0)
8db2e3ef 423 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
cdaed652 424 if (m_right.GetCount() > 0)
8db2e3ef 425 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
cdaed652
VZ
426}
427
8db2e3ef 428int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
cdaed652 429{
603f702b 430 int i;
cdaed652
VZ
431 if (array.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE;
433 i = SearchAdjacentRect(array, pt.y);
603f702b 434 if (i < 0 || i >= (int) array.GetCount())
cdaed652 435 return wxRICHTEXT_HITTEST_NONE;
603f702b
JS
436 if (!array[i]->anchor->IsShown())
437 return wxRICHTEXT_HITTEST_NONE;
438
cdaed652
VZ
439 wxPoint point = array[i]->anchor->GetPosition();
440 wxSize size = array[i]->anchor->GetCachedSize();
441 if (point.x <= pt.x && point.x + size.x >= pt.x
442 && point.y <= pt.y && point.y + size.y >= pt.y)
443 {
444 textPosition = array[i]->anchor->GetRange().GetStart();
603f702b 445 * obj = array[i]->anchor;
cdaed652
VZ
446 if (pt.x > (pt.x + pt.x + size.x) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE;
448 else
449 return wxRICHTEXT_HITTEST_AFTER;
450 }
ce00f59b 451
cdaed652
VZ
452 return wxRICHTEXT_HITTEST_NONE;
453}
454
8db2e3ef 455int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
cdaed652 456{
8db2e3ef 457 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
458 if (ret == wxRICHTEXT_HITTEST_NONE)
459 {
8db2e3ef 460 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
461 }
462 return ret;
463}
464
ce00f59b 465// Helpers for efficiency
ecb5fbf1
JS
466inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
467{
ecb5fbf1
JS
468 dc.SetFont(font);
469}
470
471inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
472{
473 const wxPen& pen1 = dc.GetPen();
474 if (pen1.IsOk() && pen.IsOk())
475 {
476 if (pen1.GetWidth() == pen.GetWidth() &&
477 pen1.GetStyle() == pen.GetStyle() &&
478 pen1.GetColour() == pen.GetColour())
479 return;
480 }
481 dc.SetPen(pen);
482}
483
484inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
485{
486 const wxBrush& brush1 = dc.GetBrush();
487 if (brush1.IsOk() && brush.IsOk())
488 {
489 if (brush1.GetStyle() == brush.GetStyle() &&
490 brush1.GetColour() == brush.GetColour())
491 return;
492 }
493 dc.SetBrush(brush);
494}
495
5d7836c4
JS
496/*!
497 * wxRichTextObject
498 * This is the base for drawable objects.
499 */
500
501IMPLEMENT_CLASS(wxRichTextObject, wxObject)
502
503wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
504{
5d7836c4
JS
505 m_refCount = 1;
506 m_parent = parent;
5d7836c4 507 m_descent = 0;
603f702b 508 m_show = true;
5d7836c4
JS
509}
510
511wxRichTextObject::~wxRichTextObject()
512{
513}
514
515void wxRichTextObject::Dereference()
516{
517 m_refCount --;
518 if (m_refCount <= 0)
519 delete this;
520}
521
522/// Copy
523void wxRichTextObject::Copy(const wxRichTextObject& obj)
524{
525 m_size = obj.m_size;
603f702b
JS
526 m_maxSize = obj.m_maxSize;
527 m_minSize = obj.m_minSize;
5d7836c4 528 m_pos = obj.m_pos;
5d7836c4 529 m_range = obj.m_range;
603f702b 530 m_ownRange = obj.m_ownRange;
5d7836c4 531 m_attributes = obj.m_attributes;
bec80f4f 532 m_properties = obj.m_properties;
5d7836c4 533 m_descent = obj.m_descent;
603f702b
JS
534 m_show = obj.m_show;
535}
536
537// Get/set the top-level container of this object.
538wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
539{
540 const wxRichTextObject* p = this;
541 while (p)
542 {
543 if (p->IsTopLevel())
544 {
545 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
546 }
547 p = p->GetParent();
548 }
549 return NULL;
5d7836c4
JS
550}
551
552void wxRichTextObject::SetMargins(int margin)
553{
603f702b 554 SetMargins(margin, margin, margin, margin);
5d7836c4
JS
555}
556
557void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
558{
603f702b
JS
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
563}
564
565int wxRichTextObject::GetLeftMargin() const
566{
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
568}
569
570int wxRichTextObject::GetRightMargin() const
571{
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
573}
574
575int wxRichTextObject::GetTopMargin() const
576{
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
578}
579
580int wxRichTextObject::GetBottomMargin() const
581{
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
583}
584
585// Calculate the available content space in the given rectangle, given the
586// margins, border and padding specified in the object's attributes.
8db2e3ef 587wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
603f702b
JS
588{
589 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590 marginRect = outerRect;
8db2e3ef
JS
591 wxRichTextAttr attr(GetAttributes());
592 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
594 return contentRect;
595}
596
597// Invalidate the buffer. With no argument, invalidates whole buffer.
598void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
599{
600 if (invalidRange != wxRICHTEXT_NONE)
601 {
23698b12
JS
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
e12b91a3 605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
23698b12 606 SetCachedSize(wxDefaultSize);
603f702b
JS
607 SetMaxSize(wxDefaultSize);
608 SetMinSize(wxDefaultSize);
609 }
5d7836c4
JS
610}
611
44219ff0 612// Convert units in tenths of a millimetre to device units
cdaed652 613int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
5d7836c4 614{
44219ff0 615 // Unscale
bec80f4f
JS
616 double scale = 1.0;
617 if (GetBuffer())
32423dd8 618 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
bec80f4f
JS
619 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
620
44219ff0
JS
621 return p;
622}
623
624// Convert units in tenths of a millimetre to device units
bec80f4f 625int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
44219ff0 626{
5d7836c4
JS
627 // There are ppi pixels in 254.1 "1/10 mm"
628
629 double pixels = ((double) units * (double)ppi) / 254.1;
bec80f4f
JS
630 if (scale != 1.0)
631 pixels /= scale;
5d7836c4 632
603f702b
JS
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels == 0 && units > 0)
635 pixels = 1;
636
5d7836c4
JS
637 return (int) pixels;
638}
639
24777478
JS
640// Convert units in pixels to tenths of a millimetre
641int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
642{
643 int p = pixels;
bec80f4f
JS
644 double scale = 1.0;
645 if (GetBuffer())
646 scale = GetBuffer()->GetScale();
647
648 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
24777478
JS
649}
650
bec80f4f 651int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
24777478
JS
652{
653 // There are ppi pixels in 254.1 "1/10 mm"
bec80f4f
JS
654
655 double p = double(pixels);
656
657 if (scale != 1.0)
658 p *= scale;
659
660 int units = int( p * 254.1 / (double) ppi );
24777478
JS
661 return units;
662}
663
bec80f4f 664// Draw the borders and background for the given rectangle and attributes.
603f702b
JS
665// Width and height are taken to be the outer margin size, not the content.
666bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
bec80f4f
JS
667{
668 // Assume boxRect is the area around the content
603f702b
JS
669 wxRect marginRect = boxRect;
670 wxRect contentRect, borderRect, paddingRect, outlineRect;
bec80f4f 671
603f702b 672 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
bec80f4f
JS
673
674 // Margin is transparent. Draw background from margin.
603f702b 675 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
bec80f4f 676 {
603f702b
JS
677 wxColour colour;
678 if (flags & wxRICHTEXT_DRAW_SELECTED)
679 {
680 // TODO: get selection colour from control?
681 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
682 }
683 else
684 colour = attr.GetBackgroundColour();
685
686 wxPen pen(colour);
687 wxBrush brush(colour);
bec80f4f
JS
688
689 dc.SetPen(pen);
690 dc.SetBrush(brush);
37e7b783 691 dc.DrawRectangle(borderRect);
bec80f4f
JS
692 }
693
603f702b
JS
694 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
695 {
696 wxRichTextAttr editBorderAttr = attr;
697 // TODO: make guideline colour configurable
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
701
702 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
703 }
704
705 if (attr.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
bec80f4f 707
603f702b
JS
708 if (attr.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
bec80f4f
JS
710
711 return true;
712}
713
714// Draw a border
603f702b 715bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
bec80f4f
JS
716{
717 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
603f702b 718 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 719
603f702b 720 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
721 {
722 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723 wxColour col(attr.GetLeft().GetColour());
724
725 // If pen width is > 1, resorts to a solid rectangle.
726 if (borderLeft == 1)
727 {
728 int penStyle = wxSOLID;
729 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
730 penStyle = wxDOT;
731 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732 penStyle = wxLONG_DASH;
603f702b 733 wxPen pen(col, 1, penStyle);
bec80f4f
JS
734 dc.SetPen(pen);
735 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
736
737 }
738 else if (borderLeft > 1)
739 {
740 wxPen pen(col);
741 wxBrush brush(col);
742 dc.SetPen(pen);
743 dc.SetBrush(brush);
744 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
745 }
746 }
747
603f702b 748 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
749 {
750 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
751
752 wxColour col(attr.GetRight().GetColour());
753
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight == 1)
756 {
757 int penStyle = wxSOLID;
758 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
759 penStyle = wxDOT;
760 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761 penStyle = wxLONG_DASH;
603f702b 762 wxPen pen(col, 1, penStyle);
bec80f4f 763 dc.SetPen(pen);
603f702b 764 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
bec80f4f
JS
765
766 }
767 else if (borderRight > 1)
768 {
769 wxPen pen(col);
770 wxBrush brush(col);
771 dc.SetPen(pen);
772 dc.SetBrush(brush);
63af79de 773 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
bec80f4f
JS
774 }
775 }
776
603f702b 777 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
778 {
779 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
780
781 wxColour col(attr.GetTop().GetColour());
782
783 // If pen width is > 1, resorts to a solid rectangle.
784 if (borderTop == 1)
785 {
786 int penStyle = wxSOLID;
787 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
788 penStyle = wxDOT;
789 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790 penStyle = wxLONG_DASH;
603f702b 791 wxPen pen(col, 1, penStyle);
bec80f4f
JS
792 dc.SetPen(pen);
793 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
794
795 }
796 else if (borderTop > 1)
797 {
798 wxPen pen(col);
799 wxBrush brush(col);
800 dc.SetPen(pen);
801 dc.SetBrush(brush);
802 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
803 }
804 }
805
603f702b 806 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
807 {
808 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
914a4e23 809 wxColour col(attr.GetBottom().GetColour());
bec80f4f
JS
810
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom == 1)
813 {
814 int penStyle = wxSOLID;
815 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
816 penStyle = wxDOT;
817 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818 penStyle = wxLONG_DASH;
603f702b 819 wxPen pen(col, 1, penStyle);
bec80f4f
JS
820 dc.SetPen(pen);
821 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
822
823 }
824 else if (borderBottom > 1)
825 {
826 wxPen pen(col);
827 wxBrush brush(col);
828 dc.SetPen(pen);
829 dc.SetBrush(brush);
63af79de 830 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
bec80f4f
JS
831 }
832 }
833
834 return true;
835}
836
837// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838// or marginRect (outer), and the other must be the default rectangle (no width or height).
839// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
840// is available.
841//
842// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843
603f702b 844bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
bec80f4f
JS
845{
846 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
850
603f702b 851 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 852
603f702b 853 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f 854 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
603f702b 855 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
bec80f4f 856 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
603f702b 857 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
bec80f4f 858 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
83c6ae8e 859 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
bec80f4f
JS
860 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
861
603f702b 862 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f 863 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
603f702b 864 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
bec80f4f 865 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
603f702b 866 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
bec80f4f 867 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
83c6ae8e 868 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
bec80f4f
JS
869 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870
603f702b 871 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
bec80f4f 872 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
603f702b 873 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
bec80f4f 874 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
603f702b 875 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
bec80f4f 876 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b 877 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
bec80f4f
JS
878 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
879
603f702b 880 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
bec80f4f 881 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
603f702b 882 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
bec80f4f 883 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
603f702b 884 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
bec80f4f 885 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
603f702b 886 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
bec80f4f
JS
887 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888
889 int leftTotal = marginLeft + borderLeft + paddingLeft;
890 int rightTotal = marginRight + borderRight + paddingRight;
891 int topTotal = marginTop + borderTop + paddingTop;
892 int bottomTotal = marginBottom + borderBottom + paddingBottom;
893
894 if (marginRect != wxRect())
895 {
896 contentRect.x = marginRect.x + leftTotal;
897 contentRect.y = marginRect.y + topTotal;
898 contentRect.width = marginRect.width - (leftTotal + rightTotal);
899 contentRect.height = marginRect.height - (topTotal + bottomTotal);
900 }
901 else
902 {
903 marginRect.x = contentRect.x - leftTotal;
904 marginRect.y = contentRect.y - topTotal;
905 marginRect.width = contentRect.width + (leftTotal + rightTotal);
906 marginRect.height = contentRect.height + (topTotal + bottomTotal);
907 }
908
909 borderRect.x = marginRect.x + marginLeft;
910 borderRect.y = marginRect.y + marginTop;
911 borderRect.width = marginRect.width - (marginLeft + marginRight);
912 borderRect.height = marginRect.height - (marginTop + marginBottom);
913
914 paddingRect.x = marginRect.x + marginLeft + borderLeft;
915 paddingRect.y = marginRect.y + marginTop + borderTop;
916 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
918
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect.x = marginRect.x - outlineLeft;
921 outlineRect.y = marginRect.y - outlineTop;
922 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
924
925 return true;
926}
927
603f702b
JS
928// Get the total margin for the object in pixels, taking into account margin, padding and border size
929bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930 int& topMargin, int& bottomMargin)
931{
932 // Assume boxRect is the area around the content
933 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934 marginRect = wxRect(0, 0, 1000, 1000);
bec80f4f 935
603f702b
JS
936 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
937
938 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939 rightMargin = marginRect.GetRight() - contentRect.GetRight();
940 topMargin = contentRect.GetTop() - marginRect.GetTop();
941 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
942
943 return true;
944}
945
946// Returns the rectangle which the child has available to it given restrictions specified in the
947// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
bb7bbd12
JS
948// availableContainerSpace might be a parent that the cell has to compute its width relative to.
949// E.g. a cell that's 50% of its parent.
950wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
603f702b
JS
951{
952 wxRect rect = availableParentSpace;
953 double scale = 1.0;
954 if (buffer)
955 scale = buffer->GetScale();
956
bb7bbd12 957 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
603f702b
JS
958
959 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
961
962 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
964
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 {
969 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
970 }
971 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 {
973 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 975 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
603f702b
JS
976 else
977 rect.x += x;
978 }
979
980 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 {
982 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
983 }
984 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 {
986 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 988 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
603f702b
JS
989 else
990 rect.y += y;
991 }
992
bb7bbd12
JS
993 if (rect.GetWidth() > availableParentSpace.GetWidth())
994 rect.SetWidth(availableParentSpace.GetWidth());
995
603f702b
JS
996 return rect;
997}
998
999// Dump to output stream for debugging
5d7836c4
JS
1000void wxRichTextObject::Dump(wxTextOutputStream& stream)
1001{
1002 stream << GetClassInfo()->GetClassName() << wxT("\n");
1003 stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
1004 stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
1005}
1006
603f702b 1007// Gets the containing buffer
44219ff0
JS
1008wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1009{
1010 const wxRichTextObject* obj = this;
345c78ca 1011 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
44219ff0
JS
1012 obj = obj->GetParent();
1013 return wxDynamicCast(obj, wxRichTextBuffer);
1014}
5d7836c4 1015
603f702b
JS
1016// Get the absolute object position, by traversing up the child/parent hierarchy
1017wxPoint wxRichTextObject::GetAbsolutePosition() const
1018{
1019 wxPoint pt = GetPosition();
1020
1021 wxRichTextObject* p = GetParent();
1022 while (p)
1023 {
1024 pt = pt + p->GetPosition();
1025 p = p->GetParent();
1026 }
1027
1028 return pt;
1029}
1030
1031// Hit-testing: returns a flag indicating hit test details, plus
1032// information about position
8db2e3ef 1033int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
603f702b
JS
1034{
1035 if (!IsShown())
1036 return wxRICHTEXT_HITTEST_NONE;
1037
1038 wxRect rect = GetRect();
1039 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040 pt.y >= rect.y && pt.y < rect.y + rect.height)
1041 {
1042 *obj = this;
1043 *contextObj = GetParentContainer();
1044 textPosition = GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON;
1046 }
1047 else
1048 return wxRICHTEXT_HITTEST_NONE;
1049}
1050
1051// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052// lays out the object again using the maximum ('best') size
8db2e3ef 1053bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
bb7bbd12
JS
1054 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
603f702b
JS
1056 int style)
1057{
bb7bbd12 1058 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
603f702b 1059 wxRect originalAvailableRect = availableChildRect;
8db2e3ef 1060 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1061
1062 wxSize maxSize = GetMaxSize();
1063
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 // on this basis
bb7bbd12 1066 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
603f702b
JS
1067 {
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL);
1070 wxRichTextAttr newAttr(attr);
1071 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1073
bb7bbd12 1074 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
603f702b
JS
1075
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
e12b91a3
JS
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
603f702b
JS
1081 {
1082 // centering, right-justification
8db2e3ef 1083 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
1084 {
1085 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1086 }
8db2e3ef 1087 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
1088 {
1089 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1090 }
1091 }
1092
8db2e3ef 1093 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1094 }
1095
1096 /*
1097 __________________
1098 | ____________ |
1099 | | | |
1100
1101
1102 */
1103
1104 return true;
1105}
1106
1107// Move the object recursively, by adding the offset from old to new
1108void wxRichTextObject::Move(const wxPoint& pt)
1109{
1110 SetPosition(pt);
1111}
1112
1113
5d7836c4
JS
1114/*!
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1117 */
1118
1119IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1120
1121wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1122 wxRichTextObject(parent)
1123{
1124}
1125
1126wxRichTextCompositeObject::~wxRichTextCompositeObject()
1127{
1128 DeleteChildren();
1129}
1130
1131/// Get the nth child
1132wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1133{
1134 wxASSERT ( n < m_children.GetCount() );
1135
1136 return m_children.Item(n)->GetData();
1137}
1138
1139/// Append a child, returning the position
1140size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1141{
1142 m_children.Append(child);
1143 child->SetParent(this);
1144 return m_children.GetCount() - 1;
1145}
1146
1147/// Insert the child in front of the given object, or at the beginning
1148bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1149{
1150 if (inFrontOf)
1151 {
1152 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1153 m_children.Insert(node, child);
1154 }
1155 else
1156 m_children.Insert(child);
1157 child->SetParent(this);
1158
1159 return true;
1160}
1161
1162/// Delete the child
1163bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1164{
1165 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1166 if (node)
1167 {
efbf6735
JS
1168 wxRichTextObject* obj = node->GetData();
1169 m_children.Erase(node);
5d7836c4 1170 if (deleteChild)
efbf6735 1171 delete obj;
5d7836c4
JS
1172
1173 return true;
1174 }
1175 return false;
1176}
1177
1178/// Delete all children
1179bool wxRichTextCompositeObject::DeleteChildren()
1180{
1181 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1182 while (node)
1183 {
1184 wxRichTextObjectList::compatibility_iterator oldNode = node;
1185
1186 wxRichTextObject* child = node->GetData();
1187 child->Dereference(); // Only delete if reference count is zero
1188
1189 node = node->GetNext();
efbf6735 1190 m_children.Erase(oldNode);
5d7836c4
JS
1191 }
1192
1193 return true;
1194}
1195
1196/// Get the child count
1197size_t wxRichTextCompositeObject::GetChildCount() const
1198{
1199 return m_children.GetCount();
1200}
1201
1202/// Copy
1203void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1204{
1205 wxRichTextObject::Copy(obj);
1206
1207 DeleteChildren();
1208
1209 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1210 while (node)
1211 {
1212 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
1213 wxRichTextObject* newChild = child->Clone();
1214 newChild->SetParent(this);
1215 m_children.Append(newChild);
5d7836c4
JS
1216
1217 node = node->GetNext();
1218 }
1219}
1220
1221/// Hit-testing: returns a flag indicating hit test details, plus
1222/// information about position
8db2e3ef 1223int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 1224{
603f702b
JS
1225 if (!IsShown())
1226 return wxRICHTEXT_HITTEST_NONE;
1227
5d7836c4
JS
1228 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1229 while (node)
1230 {
1231 wxRichTextObject* child = node->GetData();
1232
603f702b
JS
1233 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1234 {
1235 // Just check if we hit the overall object
8db2e3ef 1236 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1237 if (ret != wxRICHTEXT_HITTEST_NONE)
1238 return ret;
1239 }
1240 else if (child->IsShown())
1241 {
8db2e3ef 1242 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1243 if (ret != wxRICHTEXT_HITTEST_NONE)
1244 return ret;
1245 }
5d7836c4
JS
1246
1247 node = node->GetNext();
1248 }
1249
603f702b 1250 return wxRICHTEXT_HITTEST_NONE;
5d7836c4
JS
1251}
1252
1253/// Finds the absolute position and row height for the given character position
8db2e3ef 1254bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
1255{
1256 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1257 while (node)
1258 {
1259 wxRichTextObject* child = node->GetData();
1260
603f702b
JS
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
8db2e3ef 1265 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
5d7836c4
JS
1266 return true;
1267
1268 node = node->GetNext();
1269 }
1270
1271 return false;
1272}
1273
1274/// Calculate range
1275void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1276{
1277 long current = start;
1278 long lastEnd = current;
1279
603f702b
JS
1280 if (IsTopLevel())
1281 {
1282 current = 0;
1283 lastEnd = 0;
1284 }
1285
5d7836c4
JS
1286 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1287 while (node)
1288 {
1289 wxRichTextObject* child = node->GetData();
1290 long childEnd = 0;
1291
1292 child->CalculateRange(current, childEnd);
1293 lastEnd = childEnd;
1294
1295 current = childEnd + 1;
1296
1297 node = node->GetNext();
1298 }
1299
603f702b
JS
1300 if (IsTopLevel())
1301 {
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1304 end = start;
1305 m_range.SetRange(start, start);
5d7836c4 1306
603f702b
JS
1307 // An object with no children has zero length
1308 if (m_children.GetCount() == 0)
1309 lastEnd --;
1310 m_ownRange.SetRange(0, lastEnd);
1311 }
1312 else
1313 {
1314 end = lastEnd;
5d7836c4 1315
603f702b
JS
1316 // An object with no children has zero length
1317 if (m_children.GetCount() == 0)
1318 end --;
1319
1320 m_range.SetRange(start, end);
1321 }
5d7836c4
JS
1322}
1323
1324/// Delete range from layout.
1325bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1326{
1327 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1328
5d7836c4
JS
1329 while (node)
1330 {
1331 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1332 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1333
5d7836c4
JS
1334 // Delete the range in each paragraph
1335
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
7fe8059f 1342
5d7836c4
JS
1343 if (!obj->GetRange().IsOutside(range))
1344 {
603f702b
JS
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj->IsTopLevel())
1347 obj->DeleteRange(range);
5d7836c4
JS
1348
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj->IsEmpty() ||
1351 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1352 {
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
7fe8059f 1355 RemoveChild(obj, true);
5d7836c4
JS
1356 }
1357 }
7fe8059f 1358
5d7836c4
JS
1359 node = next;
1360 }
7fe8059f 1361
5d7836c4
JS
1362 return true;
1363}
1364
1365/// Get any text in this object for the given range
1366wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1367{
1368 wxString text;
1369 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1370 while (node)
1371 {
1372 wxRichTextObject* child = node->GetData();
1373 wxRichTextRange childRange = range;
1374 if (!child->GetRange().IsOutside(range))
1375 {
1376 childRange.LimitTo(child->GetRange());
7fe8059f 1377
5d7836c4 1378 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1379
5d7836c4
JS
1380 text += childText;
1381 }
1382 node = node->GetNext();
1383 }
1384
1385 return text;
1386}
1387
603f702b
JS
1388/// Get the child object at the given character position
1389wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1390{
1391 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1392 while (node)
1393 {
1394 wxRichTextObject* child = node->GetData();
1395 if (child->GetRange().GetStart() == pos)
1396 return child;
1397 node = node->GetNext();
1398 }
1399 return NULL;
1400}
1401
5d7836c4 1402/// Recursively merge all pieces that can be merged.
f7667b84 1403bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range)
5d7836c4
JS
1404{
1405 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1406 while (node)
1407 {
1408 wxRichTextObject* child = node->GetData();
5cb0b827 1409 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
5d7836c4 1410 {
109bfc88
JS
1411 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1412 if (composite)
f7667b84 1413 composite->Defragment(context);
109bfc88 1414
f7667b84
JS
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context.GetVirtualAttributesEnabled())
5d7836c4 1419 {
f7667b84 1420 if (node->GetNext())
109bfc88 1421 {
f7667b84
JS
1422 wxRichTextObject* nextChild = node->GetNext()->GetData();
1423 if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
1424 {
1425 nextChild->Dereference();
1426 m_children.Erase(node->GetNext());
1427 }
1428 else
1429 node = node->GetNext();
109bfc88
JS
1430 }
1431 else
1432 node = node->GetNext();
5d7836c4
JS
1433 }
1434 else
f7667b84
JS
1435 {
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1439 // at a time.
1440 wxRichTextObject* childAfterSplit = child;
1441 if (child->CanSplit(context))
1442 {
1443 childAfterSplit = child->Split(context);
1444 node = m_children.Find(childAfterSplit);
1445 }
1446
1447 if (node->GetNext())
1448 {
1449 wxRichTextObject* nextChild = node->GetNext()->GetData();
1450 wxRichTextObjectList::compatibility_iterator nextNode = node->GetNext();
1451
1452 // First split child and nextChild so we have smaller fragments to merge.
1453 // Then Merge only has to test per-object virtual attributes
1454 // because for an object with all the same sub-object attributes,
1455 // then any general virtual attributes should be merged with sub-objects by
1456 // the implementation.
1457
1458 wxRichTextObject* nextChildAfterSplit = nextChild;
1459
1460 if (nextChildAfterSplit->CanSplit(context))
1461 nextChildAfterSplit = nextChild->Split(context);
1462
1463 bool splitNextChild = nextChild != nextChildAfterSplit;
1464
1465 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1466 // Note that we use nextChild because if we had split nextChild, the first object always
1467 // remains (and further parts are appended). However we must use childAfterSplit since
1468 // it's the last part of a possibly split child.
1469
1470 if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
1471 {
1472 nextChild->Dereference();
1473 m_children.Erase(node->GetNext());
1474
1475 // Don't set node -- we'll see if we can merge again with the next
1476 // child. UNLESS we split this or the next child, in which case we know we have to
1477 // move on to the end of the next child.
1478 if (splitNextChild)
1479 node = m_children.Find(nextChildAfterSplit);
1480 }
1481 else
1482 {
1483 if (splitNextChild)
1484 node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
1485 else
1486 node = node->GetNext();
1487 }
1488 }
1489 else
1490 node = node->GetNext();
1491 }
5d7836c4
JS
1492 }
1493 else
1494 node = node->GetNext();
1495 }
1496
bec80f4f
JS
1497 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1498 if (GetChildCount() > 1)
5d7836c4 1499 {
bec80f4f
JS
1500 node = m_children.GetFirst();
1501 while (node)
1502 {
1503 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1504 wxRichTextObject* child = node->GetData();
1505 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1506 {
1507 if (child->IsEmpty())
1508 {
1509 child->Dereference();
1510 m_children.Erase(node);
1511 }
1512 node = next;
1513 }
1514 else
1515 node = node->GetNext();
1516 }
5d7836c4 1517 }
5d7836c4 1518
5d7836c4
JS
1519 return true;
1520}
1521
bec80f4f
JS
1522/// Dump to output stream for debugging
1523void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1524{
1525 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1526 while (node)
1527 {
1528 wxRichTextObject* child = node->GetData();
bec80f4f 1529 child->Dump(stream);
5d7836c4
JS
1530 node = node->GetNext();
1531 }
5d7836c4
JS
1532}
1533
603f702b
JS
1534/// Get/set the object size for the given range. Returns false if the range
1535/// is invalid for this object.
914a4e23 1536bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
603f702b
JS
1537{
1538 if (!range.IsWithin(GetRange()))
1539 return false;
5d7836c4 1540
603f702b 1541 wxSize sz;
5d7836c4 1542
603f702b
JS
1543 wxArrayInt childExtents;
1544 wxArrayInt* p;
1545 if (partialExtents)
1546 p = & childExtents;
1547 else
1548 p = NULL;
5d7836c4 1549
603f702b
JS
1550 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1551 while (node)
cdaed652 1552 {
603f702b
JS
1553 wxRichTextObject* child = node->GetData();
1554 if (!child->GetRange().IsOutside(range))
1555 {
1556 // Floating objects have a zero size within the paragraph.
e12b91a3 1557 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
603f702b
JS
1558 {
1559 if (partialExtents)
1560 {
1561 int lastSize;
1562 if (partialExtents->GetCount() > 0)
1563 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1564 else
1565 lastSize = 0;
cdaed652 1566
603f702b
JS
1567 partialExtents->Add(0 /* zero size */ + lastSize);
1568 }
1569 }
1570 else
1571 {
1572 wxSize childSize;
5d7836c4 1573
603f702b
JS
1574 wxRichTextRange rangeToUse = range;
1575 rangeToUse.LimitTo(child->GetRange());
1576 if (child->IsTopLevel())
1577 rangeToUse = child->GetOwnRange();
5d7836c4 1578
603f702b 1579 int childDescent = 0;
cdaed652 1580
603f702b
JS
1581 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1582 // but it's only going to be used after caching has taken place.
1583 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1584 {
1585 childDescent = child->GetDescent();
1586 childSize = child->GetCachedSize();
bec80f4f 1587
603f702b
JS
1588 sz.y = wxMax(sz.y, childSize.y);
1589 sz.x += childSize.x;
1590 descent = wxMax(descent, childDescent);
1591 }
914a4e23 1592 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
603f702b
JS
1593 {
1594 sz.y = wxMax(sz.y, childSize.y);
1595 sz.x += childSize.x;
1596 descent = wxMax(descent, childDescent);
bec80f4f 1597
603f702b
JS
1598 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1599 {
1600 child->SetCachedSize(childSize);
1601 child->SetDescent(childDescent);
1602 }
bec80f4f 1603
603f702b
JS
1604 if (partialExtents)
1605 {
1606 int lastSize;
1607 if (partialExtents->GetCount() > 0)
1608 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1609 else
1610 lastSize = 0;
bec80f4f 1611
603f702b
JS
1612 size_t i;
1613 for (i = 0; i < childExtents.GetCount(); i++)
1614 {
1615 partialExtents->Add(childExtents[i] + lastSize);
1616 }
1617 }
1618 }
1619 }
1620
1621 if (p)
1622 p->Clear();
1623 }
1624
1625 node = node->GetNext();
1626 }
1627 size = sz;
1628 return true;
1629}
1630
1631// Invalidate the buffer. With no argument, invalidates whole buffer.
1632void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1633{
1634 wxRichTextObject::Invalidate(invalidRange);
1635
1636 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1637 while (node)
1638 {
1639 wxRichTextObject* child = node->GetData();
1640 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1641 {
1642 // Skip
1643 }
1644 else if (child->IsTopLevel())
1645 {
e12b91a3 1646 if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
c4168888
JS
1647 {
1648 // Don't invalidate subhierarchy if we've already been laid out
1649 }
603f702b 1650 else
c4168888
JS
1651 {
1652 if (invalidRange == wxRICHTEXT_NONE)
1653 child->Invalidate(wxRICHTEXT_NONE);
1654 else
1655 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1656 }
603f702b
JS
1657 }
1658 else
1659 child->Invalidate(invalidRange);
1660 node = node->GetNext();
1661 }
1662}
1663
1664// Move the object recursively, by adding the offset from old to new
1665void wxRichTextCompositeObject::Move(const wxPoint& pt)
1666{
1667 wxPoint oldPos = GetPosition();
1668 SetPosition(pt);
1669 wxPoint offset = pt - oldPos;
1670
1671 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1672 while (node)
1673 {
1674 wxRichTextObject* child = node->GetData();
1675 wxPoint childPos = child->GetPosition() + offset;
1676 child->Move(childPos);
1677 node = node->GetNext();
1678 }
1679}
1680
1681
1682/*!
1683 * wxRichTextParagraphLayoutBox
1684 * This box knows how to lay out paragraphs.
1685 */
1686
1687IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1688
1689wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1690 wxRichTextCompositeObject(parent)
1691{
1692 Init();
1693}
1694
1695wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1696{
1697 if (m_floatCollector)
1698 {
1699 delete m_floatCollector;
1700 m_floatCollector = NULL;
1701 }
1702}
1703
1704/// Initialize the object.
1705void wxRichTextParagraphLayoutBox::Init()
1706{
1707 m_ctrl = NULL;
1708
1709 // For now, assume is the only box and has no initial size.
1710 m_range = wxRichTextRange(0, -1);
1711 m_ownRange = wxRichTextRange(0, -1);
1712
1713 m_invalidRange = wxRICHTEXT_ALL;
1714
603f702b
JS
1715 m_partialParagraph = false;
1716 m_floatCollector = NULL;
1717}
1718
1719void wxRichTextParagraphLayoutBox::Clear()
1720{
1721 DeleteChildren();
1722
1723 if (m_floatCollector)
1724 delete m_floatCollector;
1725 m_floatCollector = NULL;
1726 m_partialParagraph = false;
1727}
1728
1729/// Copy
1730void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1731{
1732 Clear();
1733
1734 wxRichTextCompositeObject::Copy(obj);
1735
1736 m_partialParagraph = obj.m_partialParagraph;
1737 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1738}
1739
07d4142f
JS
1740// Gather information about floating objects; only gather floats for those paragraphs that
1741// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1742// during layout.
603f702b 1743bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1744{
1745 if (m_floatCollector != NULL)
1746 delete m_floatCollector;
603f702b 1747 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652 1748 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
07d4142f
JS
1749 // Only gather floats up to the point we'll start formatting paragraphs.
1750 while (untilObj && node && node->GetData() != untilObj)
cdaed652
VZ
1751 {
1752 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1753 wxASSERT (child != NULL);
1754 if (child)
1755 m_floatCollector->CollectFloat(child);
1756 node = node->GetNext();
1757 }
ce00f59b 1758
cdaed652
VZ
1759 return true;
1760}
1761
603f702b
JS
1762// Returns the style sheet associated with the overall buffer.
1763wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1764{
1765 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1766}
1767
1768// Get the number of floating objects at this level
1769int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1770{
1771 if (m_floatCollector)
1772 return m_floatCollector->GetFloatingObjectCount();
1773 else
1774 return 0;
1775}
1776
1777// Get a list of floating objects
1778bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1779{
1780 if (m_floatCollector)
1781 {
1782 return m_floatCollector->GetFloatingObjects(objects);
1783 }
1784 else
1785 return false;
1786}
1787
1788// Calculate ranges
1789void wxRichTextParagraphLayoutBox::UpdateRanges()
1790{
1791 long start = 0;
1792 if (GetParent())
1793 start = GetRange().GetStart();
1794 long end;
1795 CalculateRange(start, end);
1796}
1797
cdaed652 1798// HitTest
8db2e3ef 1799int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1800{
603f702b
JS
1801 if (!IsShown())
1802 return wxRICHTEXT_HITTEST_NONE;
1803
cdaed652 1804 int ret = wxRICHTEXT_HITTEST_NONE;
e12b91a3 1805 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
8db2e3ef 1806 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
ce00f59b 1807
cdaed652 1808 if (ret == wxRICHTEXT_HITTEST_NONE)
8db2e3ef 1809 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
cdaed652 1810 else
603f702b
JS
1811 {
1812 *contextObj = this;
cdaed652 1813 return ret;
603f702b 1814 }
cdaed652
VZ
1815}
1816
1817/// Draw the floating objects
8db2e3ef 1818void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652 1819{
e12b91a3 1820 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
8db2e3ef 1821 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
cdaed652
VZ
1822}
1823
bec80f4f 1824void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1825{
1826 if (from == to)
1827 return;
1828
1829 from->RemoveChild(obj);
1830 to->AppendChild(obj);
5d7836c4
JS
1831}
1832
1833/// Draw the item
8db2e3ef 1834bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1835{
603f702b
JS
1836 if (!IsShown())
1837 return true;
1838
1839 wxRect thisRect(GetPosition(), GetCachedSize());
1840
8db2e3ef
JS
1841 wxRichTextAttr attr(GetAttributes());
1842 context.ApplyVirtualAttributes(attr, this);
1843
603f702b
JS
1844 int flags = style;
1845 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1846 flags |= wxRICHTEXT_DRAW_SELECTED;
1847
1848 // Don't draw guidelines if at top level
1849 int theseFlags = flags;
1850 if (!GetParent())
1851 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
8db2e3ef 1852 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
603f702b 1853
e12b91a3
JS
1854 if (wxRichTextBuffer::GetFloatingLayoutMode())
1855 DrawFloats(dc, context, range, selection, rect, descent, style);
1856
5d7836c4
JS
1857 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1858 while (node)
1859 {
603f702b 1860 wxRichTextObject* child = node->GetData();
7fe8059f 1861
5d7836c4
JS
1862 if (child && !child->GetRange().IsOutside(range))
1863 {
1864 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1865 wxRichTextRange childRange = range;
1866 if (child->IsTopLevel())
1867 {
1868 childRange = child->GetOwnRange();
1869 }
7fe8059f 1870
ea160b2e
JS
1871 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1872 {
1873 // Stop drawing
1874 break;
1875 }
1876 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1877 {
1878 // Skip
1879 }
1880 else
8db2e3ef 1881 child->Draw(dc, context, childRange, selection, rect, descent, style);
5d7836c4
JS
1882 }
1883
1884 node = node->GetNext();
1885 }
1886 return true;
1887}
1888
1889/// Lay the item out
8db2e3ef 1890bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 1891{
603f702b
JS
1892 SetPosition(rect.GetPosition());
1893
1894 if (!IsShown())
1895 return true;
1896
4d551ad5
JS
1897 wxRect availableSpace;
1898 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1899
8db2e3ef
JS
1900 wxRichTextAttr attr(GetAttributes());
1901 context.ApplyVirtualAttributes(attr, this);
1902
4d551ad5 1903 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1904 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1905 // so that during a size, only the visible part will be relaid out, or
1906 // it would take too long causing flicker. As an approximation, we assume that
1907 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1908 if (formatRect)
1909 {
603f702b 1910 wxRect rect2(0, 0, rect.width, rect.height);
8db2e3ef 1911 availableSpace = GetAvailableContentArea(dc, context, rect2);
4d551ad5
JS
1912
1913 // Invalidate the part of the buffer from the first visible line
1914 // to the end. If other parts of the buffer are currently invalid,
1915 // then they too will be taken into account if they are above
1916 // the visible point.
1917 long startPos = 0;
1918 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1919 if (line)
1920 startPos = line->GetAbsoluteRange().GetStart();
1921
603f702b 1922 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1923 }
1924 else
603f702b 1925 {
8db2e3ef 1926 availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
1927 }
1928
d157d142
JS
1929 // Fix the width if we're at the top level
1930 if (!GetParent())
1931 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1932
603f702b 1933 int leftMargin, rightMargin, topMargin, bottomMargin;
8db2e3ef 1934 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
603f702b 1935 topMargin, bottomMargin);
5d7836c4
JS
1936
1937 int maxWidth = 0;
603f702b
JS
1938 int maxHeight = 0;
1939
1940 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1941 int maxMaxWidth = 0;
1942
1943 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1944 int maxMinWidth = 0;
1945
1946 // If we have vertical alignment, we must recalculate everything.
8db2e3ef
JS
1947 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1948 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1949
5d7836c4 1950 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1951
38113684 1952 bool layoutAll = true;
1e967276 1953
38113684
JS
1954 // Get invalid range, rounding to paragraph start/end.
1955 wxRichTextRange invalidRange = GetInvalidRange(true);
1956
4d551ad5 1957 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1958 return true;
1959
603f702b 1960 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1961 layoutAll = true;
38113684 1962 else // If we know what range is affected, start laying out from that point on.
603f702b 1963 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1964 {
38113684 1965 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1966 if (firstParagraph)
1967 {
1968 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1969 wxRichTextObjectList::compatibility_iterator previousNode;
1970 if ( firstNode )
1971 previousNode = firstNode->GetPrevious();
9b4af7b7 1972 if (firstNode)
2c375f42 1973 {
9b4af7b7
JS
1974 if (previousNode)
1975 {
1976 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1977 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1978 }
7fe8059f 1979
2c375f42
JS
1980 // Now we're going to start iterating from the first affected paragraph.
1981 node = firstNode;
1e967276
JS
1982
1983 layoutAll = false;
2c375f42
JS
1984 }
1985 }
1986 }
1987
07d4142f
JS
1988 // Gather information about only those floating objects that will not be formatted,
1989 // after which floats will be gathered per-paragraph during layout.
e12b91a3
JS
1990 if (wxRichTextBuffer::GetFloatingLayoutMode())
1991 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1992
4d551ad5
JS
1993 // A way to force speedy rest-of-buffer layout (the 'else' below)
1994 bool forceQuickLayout = false;
39a1c2f2 1995
d3f6b1b5
JS
1996 // First get the size of the paragraphs we won't be laying out
1997 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1998 while (n && n != node)
1999 {
2000 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
2001 if (child)
2002 {
2003 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2004 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2005 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2006 }
2007 n = n->GetNext();
2008 }
2009
5d7836c4
JS
2010 while (node)
2011 {
2012 // Assume this box only contains paragraphs
2013
2014 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
706465df
JS
2015 // Unsure if this is needed
2016 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 2017
603f702b 2018 if (child && child->IsShown())
2c375f42 2019 {
603f702b
JS
2020 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2021 if ( !forceQuickLayout &&
2022 (layoutAll ||
2023 child->GetLines().IsEmpty() ||
2024 !child->GetRange().IsOutside(invalidRange)) )
2025 {
2026 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2027 // lays out the object again using the minimum size
8db2e3ef
JS
2028 child->LayoutToBestSize(dc, context, GetBuffer(),
2029 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2030
2031 // Layout must set the cached size
2032 availableSpace.y += child->GetCachedSize().y;
2033 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2034 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2035 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2036
2037 // If we're just formatting the visible part of the buffer,
2038 // and we're now past the bottom of the window, and we don't have any
2039 // floating objects (since they may cause wrapping to change for the rest of the
2040 // the buffer), start quick layout.
2041 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
2042 forceQuickLayout = true;
2043 }
2044 else
2045 {
2046 // We're outside the immediately affected range, so now let's just
2047 // move everything up or down. This assumes that all the children have previously
2048 // been laid out and have wrapped line lists associated with them.
2049 // TODO: check all paragraphs before the affected range.
2050
2051 int inc = availableSpace.y - child->GetPosition().y;
2052
2053 while (node)
2054 {
2055 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2056 if (child)
2057 {
2058 if (child->GetLines().GetCount() == 0)
2059 {
2060 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2061 // lays out the object again using the minimum size
8db2e3ef
JS
2062 child->LayoutToBestSize(dc, context, GetBuffer(),
2063 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2064
2065 //child->Layout(dc, availableChildRect, style);
2066 }
2067 else
2068 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 2069
603f702b
JS
2070 availableSpace.y += child->GetCachedSize().y;
2071 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2072 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2073 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2074 }
4d551ad5 2075
603f702b
JS
2076 node = node->GetNext();
2077 }
2078 break;
2079 }
2c375f42 2080 }
7fe8059f 2081
603f702b
JS
2082 node = node->GetNext();
2083 }
2084
2085 node = m_children.GetLast();
2086 if (node && node->GetData()->IsShown())
2087 {
2088 wxRichTextObject* child = node->GetData();
603f702b
JS
2089 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2090 }
2091 else
2092 maxHeight = 0; // topMargin + bottomMargin;
2093
23698b12 2094 // Check the bottom edge of any floating object
e12b91a3 2095 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
23698b12
JS
2096 {
2097 int bottom = GetFloatCollector()->GetLastRectBottom();
2098 if (bottom > maxHeight)
2099 maxHeight = bottom;
2100 }
2101
8db2e3ef 2102 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
bb7bbd12 2103 {
8db2e3ef 2104 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
bb7bbd12
JS
2105 int w = r.GetWidth();
2106
2107 // Convert external to content rect
2108 w = w - leftMargin - rightMargin;
2109 maxWidth = wxMax(maxWidth, w);
2110 maxMaxWidth = wxMax(maxMaxWidth, w);
2111 }
32423dd8
JS
2112 else
2113 {
2114 // TODO: Make sure the layout box's position reflects
2115 // the position of the children, but without
2116 // breaking layout of a box within a paragraph.
2117 }
bb7bbd12 2118
603f702b
JS
2119 // TODO: (also in para layout) should set the
2120 // object's size to an absolute one if specified,
2121 // but if not specified, calculate it from content.
2122
2123 // We need to add back the margins etc.
2124 {
2125 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2126 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
8db2e3ef 2127 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2128 SetCachedSize(marginRect.GetSize());
2129 }
2130
2131 // The maximum size is the greatest of all maximum widths for all paragraphs.
2132 {
2133 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2134 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2135 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2136 SetMaxSize(marginRect.GetSize());
2137 }
2138
2139 // The minimum size is the greatest of all minimum widths for all paragraphs.
2140 {
2141 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2142 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2143 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2144 SetMinSize(marginRect.GetSize());
2145 }
2146
8db2e3ef
JS
2147 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2148 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
603f702b
JS
2149 {
2150 int yOffset = 0;
2151 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2152 if (leftOverSpace > 0)
2153 {
8db2e3ef 2154 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
603f702b
JS
2155 {
2156 yOffset = (leftOverSpace/2);
2157 }
8db2e3ef 2158 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
603f702b
JS
2159 {
2160 yOffset = leftOverSpace;
2161 }
2162 }
7fe8059f 2163
603f702b
JS
2164 // Move all the children to vertically align the content
2165 // This doesn't take into account floating objects, unfortunately.
2166 if (yOffset != 0)
2167 {
2168 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
2169 while (node)
2170 {
2171 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2172 if (child)
603f702b 2173 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
2174
2175 node = node->GetNext();
2c375f42 2176 }
2c375f42 2177 }
5d7836c4
JS
2178 }
2179
1e967276 2180 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2181
2182 return true;
2183}
2184
5d7836c4 2185/// Get/set the size for the given range.
914a4e23 2186bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* WXUNUSED(partialExtents)) const
5d7836c4
JS
2187{
2188 wxSize sz;
2189
09f14108
JS
2190 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2191 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2192
2193 // First find the first paragraph whose starting position is within the range.
2194 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2195 while (node)
2196 {
2197 // child is a paragraph
2198 wxRichTextObject* child = node->GetData();
2199 const wxRichTextRange& r = child->GetRange();
2200
2201 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2202 {
2203 startPara = node;
2204 break;
2205 }
2206
2207 node = node->GetNext();
2208 }
2209
2210 // Next find the last paragraph containing part of the range
2211 node = m_children.GetFirst();
2212 while (node)
2213 {
2214 // child is a paragraph
2215 wxRichTextObject* child = node->GetData();
2216 const wxRichTextRange& r = child->GetRange();
2217
2218 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2219 {
2220 endPara = node;
2221 break;
2222 }
2223
2224 node = node->GetNext();
2225 }
2226
2227 if (!startPara || !endPara)
2228 return false;
2229
2230 // Now we can add up the sizes
2231 for (node = startPara; node ; node = node->GetNext())
2232 {
2233 // child is a paragraph
2234 wxRichTextObject* child = node->GetData();
2235 const wxRichTextRange& childRange = child->GetRange();
2236 wxRichTextRange rangeToFind = range;
2237 rangeToFind.LimitTo(childRange);
2238
603f702b
JS
2239 if (child->IsTopLevel())
2240 rangeToFind = child->GetOwnRange();
2241
5d7836c4
JS
2242 wxSize childSize;
2243
2244 int childDescent = 0;
914a4e23 2245 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize);
5d7836c4
JS
2246
2247 descent = wxMax(childDescent, descent);
2248
2249 sz.x = wxMax(sz.x, childSize.x);
2250 sz.y += childSize.y;
2251
2252 if (node == endPara)
2253 break;
2254 }
2255
2256 size = sz;
2257
2258 return true;
2259}
2260
2261/// Get the paragraph at the given position
2262wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2263{
2264 if (caretPosition)
2265 pos ++;
2266
2267 // First find the first paragraph whose starting position is within the range.
2268 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2269 while (node)
2270 {
2271 // child is a paragraph
2272 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2273 // wxASSERT (child != NULL);
5d7836c4 2274
603f702b
JS
2275 if (child)
2276 {
2277 // Return first child in buffer if position is -1
2278 // if (pos == -1)
2279 // return child;
5d7836c4 2280
603f702b
JS
2281 if (child->GetRange().Contains(pos))
2282 return child;
2283 }
5d7836c4
JS
2284
2285 node = node->GetNext();
2286 }
2287 return NULL;
2288}
2289
2290/// Get the line at the given position
2291wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2292{
2293 if (caretPosition)
2294 pos ++;
2295
2296 // First find the first paragraph whose starting position is within the range.
2297 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2298 while (node)
2299 {
7051fa41
JS
2300 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2301 if (obj->GetRange().Contains(pos))
5d7836c4 2302 {
7051fa41
JS
2303 // child is a paragraph
2304 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2305 // wxASSERT (child != NULL);
7051fa41 2306
603f702b 2307 if (child)
7051fa41 2308 {
603f702b
JS
2309 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2310 while (node2)
2311 {
2312 wxRichTextLine* line = node2->GetData();
5d7836c4 2313
603f702b 2314 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2315
603f702b 2316 if (range.Contains(pos) ||
5d7836c4 2317
603f702b
JS
2318 // If the position is end-of-paragraph, then return the last line of
2319 // of the paragraph.
2320 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2321 return line;
5d7836c4 2322
603f702b
JS
2323 node2 = node2->GetNext();
2324 }
7051fa41 2325 }
7fe8059f 2326 }
5d7836c4
JS
2327
2328 node = node->GetNext();
2329 }
2330
2331 int lineCount = GetLineCount();
2332 if (lineCount > 0)
2333 return GetLineForVisibleLineNumber(lineCount-1);
2334 else
2335 return NULL;
2336}
2337
2338/// Get the line at the given y pixel position, or the last line.
2339wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2340{
2341 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2342 while (node)
2343 {
2344 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2345 // wxASSERT (child != NULL);
5d7836c4 2346
603f702b 2347 if (child)
5d7836c4 2348 {
603f702b
JS
2349 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2350 while (node2)
2351 {
2352 wxRichTextLine* line = node2->GetData();
5d7836c4 2353
603f702b 2354 wxRect rect(line->GetRect());
5d7836c4 2355
603f702b
JS
2356 if (y <= rect.GetBottom())
2357 return line;
5d7836c4 2358
603f702b
JS
2359 node2 = node2->GetNext();
2360 }
7fe8059f 2361 }
5d7836c4
JS
2362
2363 node = node->GetNext();
2364 }
2365
2366 // Return last line
2367 int lineCount = GetLineCount();
2368 if (lineCount > 0)
2369 return GetLineForVisibleLineNumber(lineCount-1);
2370 else
2371 return NULL;
2372}
2373
2374/// Get the number of visible lines
2375int wxRichTextParagraphLayoutBox::GetLineCount() const
2376{
2377 int count = 0;
2378
2379 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2380 while (node)
2381 {
2382 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2383 // wxASSERT (child != NULL);
2384
2385 if (child)
2386 count += child->GetLines().GetCount();
5d7836c4 2387
5d7836c4
JS
2388 node = node->GetNext();
2389 }
2390 return count;
2391}
2392
2393
2394/// Get the paragraph for a given line
2395wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2396{
1e967276 2397 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2398}
2399
2400/// Get the line size at the given position
2401wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2402{
2403 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2404 if (line)
2405 {
2406 return line->GetSize();
2407 }
2408 else
2409 return wxSize(0, 0);
2410}
2411
2412
2413/// Convenience function to add a paragraph of text
24777478 2414wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2415{
fe5aa22c 2416 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2417 // be combined at display time.
2418 // Divide into paragraph and character styles.
3e541562 2419
24777478
JS
2420 wxRichTextAttr defaultCharStyle;
2421 wxRichTextAttr defaultParaStyle;
4f32b3cf 2422
5607c890
JS
2423 // If the default style is a named paragraph style, don't apply any character formatting
2424 // to the initial text string.
2425 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2426 {
2427 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2428 if (def)
2429 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2430 }
2431 else
2432 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2433
24777478
JS
2434 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2435 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2436
2437 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
32423dd8 2438 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4
JS
2439
2440 AppendChild(para);
2441
2442 UpdateRanges();
5d7836c4
JS
2443
2444 return para->GetRange();
2445}
2446
2447/// Adds multiple paragraphs, based on newlines.
24777478 2448wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2449{
fe5aa22c 2450 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2451 // be combined at display time.
2452 // Divide into paragraph and character styles.
3e541562 2453
24777478
JS
2454 wxRichTextAttr defaultCharStyle;
2455 wxRichTextAttr defaultParaStyle;
5607c890
JS
2456
2457 // If the default style is a named paragraph style, don't apply any character formatting
2458 // to the initial text string.
2459 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2460 {
2461 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2462 if (def)
2463 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2464 }
2465 else
2466 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2467
24777478
JS
2468 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2469 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2470
5d7836c4
JS
2471 wxRichTextParagraph* firstPara = NULL;
2472 wxRichTextParagraph* lastPara = NULL;
2473
2474 wxRichTextRange range(-1, -1);
0ca07313 2475
5d7836c4 2476 size_t i = 0;
28f92d74 2477 size_t len = text.length();
5d7836c4 2478 wxString line;
4f32b3cf 2479 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2480 para->GetAttributes().GetTextBoxAttr().Reset();
0ca07313
JS
2481
2482 AppendChild(para);
2483
2484 firstPara = para;
2485 lastPara = para;
2486
5d7836c4
JS
2487 while (i < len)
2488 {
2489 wxChar ch = text[i];
2490 if (ch == wxT('\n') || ch == wxT('\r'))
2491 {
99404ab0
JS
2492 if (i != (len-1))
2493 {
2494 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2495 plainText->SetText(line);
0ca07313 2496
99404ab0 2497 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2498 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4 2499
99404ab0 2500 AppendChild(para);
0ca07313 2501
99404ab0
JS
2502 lastPara = para;
2503 line = wxEmptyString;
2504 }
5d7836c4
JS
2505 }
2506 else
2507 line += ch;
2508
2509 i ++;
2510 }
0ca07313 2511
7fe8059f 2512 if (!line.empty())
5d7836c4 2513 {
0ca07313
JS
2514 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2515 plainText->SetText(line);
5d7836c4
JS
2516 }
2517
5d7836c4 2518 UpdateRanges();
0ca07313 2519
0ca07313 2520 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2521}
2522
2523/// Convenience function to add an image
24777478 2524wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2525{
fe5aa22c 2526 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2527 // be combined at display time.
2528 // Divide into paragraph and character styles.
3e541562 2529
24777478
JS
2530 wxRichTextAttr defaultCharStyle;
2531 wxRichTextAttr defaultParaStyle;
5607c890
JS
2532
2533 // If the default style is a named paragraph style, don't apply any character formatting
2534 // to the initial text string.
2535 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2536 {
2537 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2538 if (def)
2539 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2540 }
2541 else
2542 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2543
24777478
JS
2544 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2545 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2546
4f32b3cf 2547 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
32423dd8 2548 para->GetAttributes().GetTextBoxAttr().Reset();
4f32b3cf
JS
2549 AppendChild(para);
2550 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2551
5d7836c4 2552 UpdateRanges();
5d7836c4
JS
2553
2554 return para->GetRange();
2555}
2556
2557
2558/// Insert fragment into this box at the given position. If partialParagraph is true,
2559/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2560/// marker.
5d7836c4 2561
0ca07313 2562bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2563{
5d7836c4
JS
2564 // First, find the first paragraph whose starting position is within the range.
2565 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2566 if (para)
2567 {
24777478 2568 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2569
5d7836c4
JS
2570 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2571
2572 // Now split at this position, returning the object to insert the new
2573 // ones in front of.
2574 wxRichTextObject* nextObject = para->SplitAt(position);
2575
2576 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2577 // text, for example, so let's optimize.
2578
2579 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2580 {
2581 // Add the first para to this para...
2582 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2583 if (!firstParaNode)
2584 return false;
2585
2586 // Iterate through the fragment paragraph inserting the content into this paragraph.
2587 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2588 wxASSERT (firstPara != NULL);
2589
2590 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2591 while (objectNode)
2592 {
2593 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2594
5d7836c4
JS
2595 if (!nextObject)
2596 {
2597 // Append
2598 para->AppendChild(newObj);
2599 }
2600 else
2601 {
2602 // Insert before nextObject
2603 para->InsertChild(newObj, nextObject);
2604 }
7fe8059f 2605
5d7836c4
JS
2606 objectNode = objectNode->GetNext();
2607 }
2608
2609 return true;
2610 }
2611 else
2612 {
2613 // Procedure for inserting a fragment consisting of a number of
2614 // paragraphs:
2615 //
2616 // 1. Remove and save the content that's after the insertion point, for adding
2617 // back once we've added the fragment.
2618 // 2. Add the content from the first fragment paragraph to the current
2619 // paragraph.
2620 // 3. Add remaining fragment paragraphs after the current paragraph.
2621 // 4. Add back the saved content from the first paragraph. If partialParagraph
2622 // is true, add it to the last paragraph added and not a new one.
2623
2624 // 1. Remove and save objects after split point.
2625 wxList savedObjects;
2626 if (nextObject)
2627 para->MoveToList(nextObject, savedObjects);
2628
2629 // 2. Add the content from the 1st fragment paragraph.
2630 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2631 if (!firstParaNode)
2632 return false;
2633
2634 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2635 wxASSERT(firstPara != NULL);
2636
6c0ea513
JS
2637 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2638 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2639
2640 // Save empty paragraph attributes for appending later
2641 // These are character attributes deliberately set for a new paragraph. Without this,
2642 // we couldn't pass default attributes when appending a new paragraph.
24777478 2643 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2644
5d7836c4 2645 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2646
2647 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2648 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2649
5d7836c4
JS
2650 while (objectNode)
2651 {
c025e094 2652 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2653
c025e094
JS
2654 // Append
2655 para->AppendChild(newObj);
7fe8059f 2656
5d7836c4
JS
2657 objectNode = objectNode->GetNext();
2658 }
2659
2660 // 3. Add remaining fragment paragraphs after the current paragraph.
2661 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2662 wxRichTextObject* nextParagraph = NULL;
2663 if (nextParagraphNode)
2664 nextParagraph = nextParagraphNode->GetData();
2665
2666 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2667 wxRichTextParagraph* finalPara = para;
2668
99404ab0
JS
2669 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2670
5d7836c4 2671 // If there was only one paragraph, we need to insert a new one.
99404ab0 2672 while (i)
5d7836c4 2673 {
99404ab0
JS
2674 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2675 wxASSERT( para != NULL );
5d7836c4 2676
99404ab0 2677 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2678
2679 if (nextParagraph)
2680 InsertChild(finalPara, nextParagraph);
2681 else
7fe8059f 2682 AppendChild(finalPara);
99404ab0
JS
2683
2684 i = i->GetNext();
5d7836c4 2685 }
5d7836c4 2686
99404ab0
JS
2687 // If there was only one paragraph, or we have full paragraphs in our fragment,
2688 // we need to insert a new one.
2689 if (needExtraPara)
2690 {
2691 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2692
2693 if (nextParagraph)
2694 InsertChild(finalPara, nextParagraph);
2695 else
2696 AppendChild(finalPara);
5d7836c4
JS
2697 }
2698
2699 // 4. Add back the remaining content.
2700 if (finalPara)
2701 {
c025e094
JS
2702 if (nextObject)
2703 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2704
2705 // Ensure there's at least one object
2706 if (finalPara->GetChildCount() == 0)
2707 {
7fe8059f 2708 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2709 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2710
2711 finalPara->AppendChild(text);
2712 }
2713 }
2714
6c0ea513
JS
2715 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2716 finalPara->SetAttributes(firstPara->GetAttributes());
2717 else if (finalPara && finalPara != para)
99404ab0
JS
2718 finalPara->SetAttributes(originalAttr);
2719
5d7836c4
JS
2720 return true;
2721 }
2722 }
2723 else
2724 {
2725 // Append
2726 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2727 while (i)
2728 {
2729 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2730 wxASSERT( para != NULL );
7fe8059f 2731
5d7836c4 2732 AppendChild(para->Clone());
7fe8059f 2733
5d7836c4
JS
2734 i = i->GetNext();
2735 }
2736
2737 return true;
2738 }
5d7836c4
JS
2739}
2740
2741/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2742/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2743bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2744{
2745 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2746 while (i)
2747 {
2748 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2749 wxASSERT( para != NULL );
2750
2751 if (!para->GetRange().IsOutside(range))
2752 {
2753 fragment.AppendChild(para->Clone());
7fe8059f 2754 }
5d7836c4
JS
2755 i = i->GetNext();
2756 }
2757
2758 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2759 if (!fragment.IsEmpty())
2760 {
5d7836c4
JS
2761 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2762 wxASSERT( firstPara != NULL );
2763
0e190fa2
JS
2764 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2765 wxASSERT( lastPara != NULL );
2766
2767 if (!firstPara || !lastPara)
2768 return false;
2769
2770 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2771
2772 long firstPos = firstPara->GetRange().GetStart();
2773
2774 // Adjust for renumbering from zero
2775 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2776
2777 long end;
2778 fragment.CalculateRange(0, end);
2779
5d7836c4 2780 // Chop off the start of the paragraph
0e190fa2 2781 if (topTailRange.GetStart() > 0)
5d7836c4 2782 {
0e190fa2 2783 wxRichTextRange r(0, topTailRange.GetStart()-1);
5d7836c4
JS
2784 firstPara->DeleteRange(r);
2785
2786 // Make sure the numbering is correct
0e190fa2 2787 fragment.CalculateRange(0, end);
5d7836c4
JS
2788
2789 // Now, we've deleted some positions, so adjust the range
2790 // accordingly.
0e190fa2
JS
2791 topTailRange.SetStart(range.GetLength());
2792 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2793 }
2794 else
2795 {
2796 topTailRange.SetStart(range.GetLength());
2797 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
5d7836c4
JS
2798 }
2799
61e6149e 2800 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
5d7836c4 2801 {
0e190fa2 2802 lastPara->DeleteRange(topTailRange);
5d7836c4
JS
2803
2804 // Make sure the numbering is correct
2805 long end;
0e190fa2 2806 fragment.CalculateRange(0, end);
5d7836c4
JS
2807
2808 // We only have part of a paragraph at the end
2809 fragment.SetPartialParagraph(true);
2810 }
2811 else
2812 {
0e190fa2
JS
2813 // We have a partial paragraph (don't save last new paragraph marker)
2814 // or complete paragraph
2815 fragment.SetPartialParagraph(isFragment);
5d7836c4
JS
2816 }
2817 }
2818
2819 return true;
2820}
2821
2822/// Given a position, get the number of the visible line (potentially many to a paragraph),
2823/// starting from zero at the start of the buffer.
2824long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2825{
2826 if (caretPosition)
2827 pos ++;
2828
2829 int lineCount = 0;
2830
2831 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2832 while (node)
2833 {
2834 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2835 // wxASSERT( child != NULL );
5d7836c4 2836
603f702b 2837 if (child)
5d7836c4 2838 {
603f702b 2839 if (child->GetRange().Contains(pos))
5d7836c4 2840 {
603f702b
JS
2841 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2842 while (node2)
5d7836c4 2843 {
603f702b
JS
2844 wxRichTextLine* line = node2->GetData();
2845 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2846
603f702b
JS
2847 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2848 {
2849 // If the caret is displayed at the end of the previous wrapped line,
2850 // we want to return the line it's _displayed_ at (not the actual line
2851 // containing the position).
2852 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2853 return lineCount - 1;
2854 else
2855 return lineCount;
2856 }
7fe8059f 2857
603f702b
JS
2858 lineCount ++;
2859
2860 node2 = node2->GetNext();
2861 }
2862 // If we didn't find it in the lines, it must be
2863 // the last position of the paragraph. So return the last line.
2864 return lineCount-1;
5d7836c4 2865 }
603f702b
JS
2866 else
2867 lineCount += child->GetLines().GetCount();
5d7836c4 2868 }
5d7836c4
JS
2869
2870 node = node->GetNext();
2871 }
2872
2873 // Not found
2874 return -1;
2875}
2876
2877/// Given a line number, get the corresponding wxRichTextLine object.
2878wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2879{
2880 int lineCount = 0;
2881
2882 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2883 while (node)
2884 {
2885 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2886 // wxASSERT(child != NULL);
5d7836c4 2887
603f702b 2888 if (child)
5d7836c4 2889 {
603f702b 2890 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2891 {
603f702b
JS
2892 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2893 while (node2)
2894 {
2895 wxRichTextLine* line = node2->GetData();
7fe8059f 2896
603f702b
JS
2897 if (lineCount == lineNumber)
2898 return line;
5d7836c4 2899
603f702b 2900 lineCount ++;
7fe8059f 2901
603f702b
JS
2902 node2 = node2->GetNext();
2903 }
7fe8059f 2904 }
603f702b
JS
2905 else
2906 lineCount += child->GetLines().GetCount();
5d7836c4 2907 }
5d7836c4
JS
2908
2909 node = node->GetNext();
2910 }
2911
2912 // Didn't find it
2913 return NULL;
2914}
2915
2916/// Delete range from layout.
2917bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2918{
2919 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2920
99404ab0 2921 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2922 while (node)
2923 {
2924 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2925 // wxASSERT (obj != NULL);
5d7836c4
JS
2926
2927 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2928
603f702b 2929 if (obj)
5d7836c4 2930 {
603f702b 2931 // Delete the range in each paragraph
99404ab0 2932
603f702b 2933 if (!obj->GetRange().IsOutside(range))
5d7836c4 2934 {
603f702b
JS
2935 // Deletes the content of this object within the given range
2936 obj->DeleteRange(range);
99404ab0 2937
603f702b
JS
2938 wxRichTextRange thisRange = obj->GetRange();
2939 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2940
603f702b
JS
2941 // If the whole paragraph is within the range to delete,
2942 // delete the whole thing.
2943 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2944 {
603f702b
JS
2945 // Delete the whole object
2946 RemoveChild(obj, true);
2947 obj = NULL;
99404ab0 2948 }
603f702b
JS
2949 else if (!firstPara)
2950 firstPara = obj;
5d7836c4 2951
603f702b
JS
2952 // If the range includes the paragraph end, we need to join this
2953 // and the next paragraph.
2954 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2955 {
603f702b
JS
2956 // We need to move the objects from the next paragraph
2957 // to this paragraph
2958
2959 wxRichTextParagraph* nextParagraph = NULL;
2960 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2961 nextParagraph = obj;
6c0ea513 2962 else
603f702b
JS
2963 {
2964 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2965 if (next)
2966 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2967 }
5d7836c4 2968
603f702b
JS
2969 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2970
2971 wxRichTextAttr nextParaAttr;
2972 if (applyFinalParagraphStyle)
2973 {
2974 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2975 // not the next one.
2976 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2977 nextParaAttr = thisAttr;
2978 else
2979 nextParaAttr = nextParagraph->GetAttributes();
2980 }
5d7836c4 2981
603f702b 2982 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2983 {
603f702b
JS
2984 // Move the objects to the previous para
2985 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2986
603f702b
JS
2987 while (node1)
2988 {
2989 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2990
603f702b 2991 firstPara->AppendChild(obj1);
5d7836c4 2992
603f702b
JS
2993 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2994 nextParagraph->GetChildren().Erase(node1);
99404ab0 2995
603f702b
JS
2996 node1 = next1;
2997 }
5d7836c4 2998
603f702b
JS
2999 // Delete the paragraph
3000 RemoveChild(nextParagraph, true);
3001 }
fa01bfdd 3002
603f702b
JS
3003 // Avoid empty paragraphs
3004 if (firstPara && firstPara->GetChildren().GetCount() == 0)
3005 {
3006 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
3007 firstPara->AppendChild(text);
3008 }
99404ab0 3009
603f702b
JS
3010 if (applyFinalParagraphStyle)
3011 firstPara->SetAttributes(nextParaAttr);
3012
3013 return true;
3014 }
5d7836c4
JS
3015 }
3016 }
7fe8059f 3017
5d7836c4
JS
3018 node = next;
3019 }
7fe8059f 3020
5d7836c4
JS
3021 return true;
3022}
3023
3024/// Get any text in this object for the given range
3025wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
3026{
3027 int lineCount = 0;
3028 wxString text;
3029 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3030 while (node)
3031 {
3032 wxRichTextObject* child = node->GetData();
3033 if (!child->GetRange().IsOutside(range))
3034 {
5d7836c4
JS
3035 wxRichTextRange childRange = range;
3036 childRange.LimitTo(child->GetRange());
7fe8059f 3037
5d7836c4 3038 wxString childText = child->GetTextForRange(childRange);
7fe8059f 3039
5d7836c4
JS
3040 text += childText;
3041
1a75935d 3042 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
3043 text += wxT("\n");
3044
5d7836c4
JS
3045 lineCount ++;
3046 }
3047 node = node->GetNext();
3048 }
3049
3050 return text;
3051}
3052
3053/// Get all the text
3054wxString wxRichTextParagraphLayoutBox::GetText() const
3055{
c99f1b0f 3056 return GetTextForRange(GetOwnRange());
5d7836c4
JS
3057}
3058
3059/// Get the paragraph by number
3060wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
3061{
27e20452 3062 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
3063 return NULL;
3064
3065 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
3066}
3067
3068/// Get the length of the paragraph
3069int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3070{
3071 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3072 if (para)
3073 return para->GetRange().GetLength() - 1; // don't include newline
3074 else
3075 return 0;
3076}
3077
3078/// Get the text of the paragraph
3079wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3080{
3081 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3082 if (para)
3083 return para->GetTextForRange(para->GetRange());
3084 else
3085 return wxEmptyString;
3086}
3087
3088/// Convert zero-based line column and paragraph number to a position.
3089long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3090{
3091 wxRichTextParagraph* para = GetParagraphAtLine(y);
3092 if (para)
3093 {
3094 return para->GetRange().GetStart() + x;
3095 }
3096 else
3097 return -1;
3098}
3099
3100/// Convert zero-based position to line column and paragraph number
3101bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3102{
3103 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3104 if (para)
3105 {
3106 int count = 0;
3107 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3108 while (node)
3109 {
3110 wxRichTextObject* child = node->GetData();
3111 if (child == para)
3112 break;
3113 count ++;
3114 node = node->GetNext();
3115 }
3116
3117 *y = count;
3118 *x = pos - para->GetRange().GetStart();
3119
3120 return true;
3121 }
3122 else
3123 return false;
3124}
3125
3126/// Get the leaf object in a paragraph at this position.
3127/// Given a line number, get the corresponding wxRichTextLine object.
3128wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3129{
3130 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3131 if (para)
3132 {
3133 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 3134
5d7836c4
JS
3135 while (node)
3136 {
3137 wxRichTextObject* child = node->GetData();
3138 if (child->GetRange().Contains(position))
3139 return child;
7fe8059f 3140
5d7836c4
JS
3141 node = node->GetNext();
3142 }
3143 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3144 return para->GetChildren().GetLast()->GetData();
3145 }
3146 return NULL;
3147}
3148
3149/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 3150bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
3151{
3152 bool characterStyle = false;
3153 bool paragraphStyle = false;
3154
3155 if (style.IsCharacterStyle())
3156 characterStyle = true;
3157 if (style.IsParagraphStyle())
3158 paragraphStyle = true;
3159
603f702b
JS
3160 wxRichTextBuffer* buffer = GetBuffer();
3161
59509217
JS
3162 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3163 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3164 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3165 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 3166 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 3167 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
3168
3169 // Apply paragraph style first, if any
24777478 3170 wxRichTextAttr wholeStyle(style);
523d2f14 3171
603f702b 3172 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 3173 {
603f702b 3174 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 3175 if (def)
603f702b 3176 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 3177 }
59509217
JS
3178
3179 // Limit the attributes to be set to the content to only character attributes.
24777478 3180 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
3181 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3182
603f702b 3183 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 3184 {
603f702b 3185 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 3186 if (def)
603f702b 3187 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
3188 }
3189
5d7836c4
JS
3190 // If we are associated with a control, make undoable; otherwise, apply immediately
3191 // to the data.
3192
603f702b 3193 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3194
3195 wxRichTextAction* action = NULL;
7fe8059f 3196
5d7836c4
JS
3197 if (haveControl && withUndo)
3198 {
603f702b 3199 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3200 action->SetRange(range);
603f702b 3201 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3202 }
3203
3204 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3205 while (node)
3206 {
3207 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3208 // wxASSERT (para != NULL);
5d7836c4
JS
3209
3210 if (para && para->GetChildCount() > 0)
3211 {
3212 // Stop searching if we're beyond the range of interest
3213 if (para->GetRange().GetStart() > range.GetEnd())
3214 break;
3215
3216 if (!para->GetRange().IsOutside(range))
3217 {
3218 // We'll be using a copy of the paragraph to make style changes,
3219 // not updating the buffer directly.
4e09ebe8 3220 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3221
5d7836c4
JS
3222 if (haveControl && withUndo)
3223 {
3224 newPara = new wxRichTextParagraph(*para);
3225 action->GetNewParagraphs().AppendChild(newPara);
3226
3227 // Also store the old ones for Undo
3228 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3229 }
3230 else
3231 newPara = para;
41a85215 3232
a7ed48a5
JS
3233 // If we're specifying paragraphs only, then we really mean character formatting
3234 // to be included in the paragraph style
3235 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3236 {
aeb6ebe2
JS
3237 if (removeStyle)
3238 {
3239 // Removes the given style from the paragraph
3240 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3241 }
3242 else if (resetExistingStyle)
523d2f14
JS
3243 newPara->GetAttributes() = wholeStyle;
3244 else
59509217 3245 {
523d2f14
JS
3246 if (applyMinimal)
3247 {
3248 // Only apply attributes that will make a difference to the combined
3249 // style as seen on the display
603f702b 3250 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3251 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3252 }
3253 else
3254 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3255 }
59509217 3256 }
5d7836c4 3257
5912d19e 3258 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3259 // since they will computed as needed. Only apply the character styling if it's _only_
3260 // character styling. This policy is subject to change and might be put under user control.
3261
59509217
JS
3262 // Hm. we might well be applying a mix of paragraph and character styles, in which
3263 // case we _do_ want to apply character styles regardless of what para styles are set.
3264 // But if we're applying a paragraph style, which has some character attributes, but
3265 // we only want the paragraphs to hold this character style, then we _don't_ want to
3266 // apply the character style. So we need to be able to choose.
3267
f1d800d9 3268 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3269 {
3270 wxRichTextRange childRange(range);
3271 childRange.LimitTo(newPara->GetRange());
7fe8059f 3272
5d7836c4
JS
3273 // Find the starting position and if necessary split it so
3274 // we can start applying a different style.
3275 // TODO: check that the style actually changes or is different
3276 // from style outside of range
4e09ebe8
JS
3277 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3278 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3279
5d7836c4
JS
3280 if (childRange.GetStart() == newPara->GetRange().GetStart())
3281 firstObject = newPara->GetChildren().GetFirst()->GetData();
3282 else
3283 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3284
5d7836c4
JS
3285 // Increment by 1 because we're apply the style one _after_ the split point
3286 long splitPoint = childRange.GetEnd();
3287 if (splitPoint != newPara->GetRange().GetEnd())
3288 splitPoint ++;
7fe8059f 3289
5d7836c4 3290 // Find last object
4b3483e7 3291 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3292 lastObject = newPara->GetChildren().GetLast()->GetData();
3293 else
3294 // lastObject is set as a side-effect of splitting. It's
3295 // returned as the object before the new object.
3296 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3297
5d7836c4
JS
3298 wxASSERT(firstObject != NULL);
3299 wxASSERT(lastObject != NULL);
7fe8059f 3300
5d7836c4
JS
3301 if (!firstObject || !lastObject)
3302 continue;
7fe8059f 3303
5d7836c4
JS
3304 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3305 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3306
4c9847e1
MW
3307 wxASSERT(firstNode);
3308 wxASSERT(lastNode);
7fe8059f 3309
5d7836c4 3310 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3311
5d7836c4
JS
3312 while (node2)
3313 {
3314 wxRichTextObject* child = node2->GetData();
7fe8059f 3315
aeb6ebe2
JS
3316 if (removeStyle)
3317 {
3318 // Removes the given style from the paragraph
3319 wxRichTextRemoveStyle(child->GetAttributes(), style);
3320 }
3321 else if (resetExistingStyle)
523d2f14
JS
3322 child->GetAttributes() = characterAttributes;
3323 else
59509217 3324 {
523d2f14
JS
3325 if (applyMinimal)
3326 {
3327 // Only apply attributes that will make a difference to the combined
3328 // style as seen on the display
603f702b 3329 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3330 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3331 }
3332 else
3333 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3334 }
59509217 3335
5d7836c4
JS
3336 if (node2 == lastNode)
3337 break;
7fe8059f 3338
5d7836c4
JS
3339 node2 = node2->GetNext();
3340 }
3341 }
3342 }
3343 }
3344
3345 node = node->GetNext();
3346 }
3347
3348 // Do action, or delay it until end of batch.
3349 if (haveControl && withUndo)
603f702b 3350 buffer->SubmitAction(action);
5d7836c4
JS
3351
3352 return true;
3353}
3354
603f702b
JS
3355// Just change the attributes for this single object.
3356void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3357{
603f702b 3358 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3359 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3360 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3361 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3362
cdaed652 3363 wxRichTextAction *action = NULL;
603f702b
JS
3364 wxRichTextAttr newAttr = obj->GetAttributes();
3365 if (resetExistingStyle)
3366 newAttr = textAttr;
3367 else
3368 newAttr.Apply(textAttr);
cdaed652
VZ
3369
3370 if (haveControl && withUndo)
3371 {
603f702b
JS
3372 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3373 action->SetRange(obj->GetRange().FromInternal());
3374 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3375 action->MakeObject(obj);
bec80f4f 3376
603f702b 3377 action->GetAttributes() = newAttr;
cdaed652
VZ
3378 }
3379 else
603f702b 3380 obj->GetAttributes() = newAttr;
cdaed652
VZ
3381
3382 if (haveControl && withUndo)
603f702b 3383 buffer->SubmitAction(action);
cdaed652
VZ
3384}
3385
5d7836c4 3386/// Get the text attributes for this position.
24777478 3387bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3388{
fe5aa22c
JS
3389 return DoGetStyle(position, style, true);
3390}
e191ee87 3391
24777478 3392bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3393{
3394 return DoGetStyle(position, style, false);
3395}
3396
fe5aa22c
JS
3397/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3398/// context attributes.
24777478 3399bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3400{
4e09ebe8 3401 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3402
5d7836c4 3403 if (style.IsParagraphStyle())
fe5aa22c 3404 {
5d7836c4 3405 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3406 if (obj)
3407 {
fe5aa22c
JS
3408 if (combineStyles)
3409 {
3410 // Start with the base style
3411 style = GetAttributes();
32423dd8 3412 style.GetTextBoxAttr().Reset();
e191ee87 3413
fe5aa22c
JS
3414 // Apply the paragraph style
3415 wxRichTextApplyStyle(style, obj->GetAttributes());
3416 }
3417 else
3418 style = obj->GetAttributes();
5912d19e 3419
fe5aa22c
JS
3420 return true;
3421 }
5d7836c4
JS
3422 }
3423 else
fe5aa22c
JS
3424 {
3425 obj = GetLeafObjectAtPosition(position);
3426 if (obj)
3427 {
fe5aa22c
JS
3428 if (combineStyles)
3429 {
3430 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3431 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3432 }
3433 else
3434 style = obj->GetAttributes();
5912d19e 3435
fe5aa22c
JS
3436 return true;
3437 }
fe5aa22c
JS
3438 }
3439 return false;
5d7836c4
JS
3440}
3441
59509217
JS
3442static bool wxHasStyle(long flags, long style)
3443{
3444 return (flags & style) != 0;
3445}
3446
3447/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3448/// content.
24777478
JS
3449bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3450{
3451 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3452
3453 return true;
3454}
3455
3456/// Get the combined style for a range - if any attribute is different within the range,
3457/// that attribute is not present within the flags.
3458/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3459/// nested.
3460bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3461{
24777478
JS
3462 style = wxRichTextAttr();
3463
c4168888 3464 wxRichTextAttr clashingAttrPara, clashingAttrChar;
24777478 3465 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3466
24777478
JS
3467 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3468 while (node)
59509217 3469 {
603f702b
JS
3470 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3471 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3472 {
24777478 3473 if (para->GetChildren().GetCount() == 0)
59509217 3474 {
603f702b 3475 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3476
c4168888 3477 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
59509217
JS
3478 }
3479 else
3480 {
24777478
JS
3481 wxRichTextRange paraRange(para->GetRange());
3482 paraRange.LimitTo(range);
59509217 3483
24777478
JS
3484 // First collect paragraph attributes only
3485 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3486 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
c4168888 3487 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
9c4cb611 3488
24777478
JS
3489 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3490
3491 while (childNode)
59509217 3492 {
24777478
JS
3493 wxRichTextObject* child = childNode->GetData();
3494 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3495 {
603f702b 3496 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3497
24777478
JS
3498 // Now collect character attributes only
3499 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3500
c4168888 3501 CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
24777478 3502 }
59509217 3503
24777478 3504 childNode = childNode->GetNext();
59509217
JS
3505 }
3506 }
59509217 3507 }
24777478 3508 node = node->GetNext();
59509217 3509 }
24777478
JS
3510 return true;
3511}
59509217 3512
24777478
JS
3513/// Set default style
3514bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3515{
3516 m_defaultAttributes = style;
3517 return true;
3518}
59509217 3519
24777478
JS
3520/// Test if this whole range has character attributes of the specified kind. If any
3521/// of the attributes are different within the range, the test fails. You
3522/// can use this to implement, for example, bold button updating. style must have
3523/// flags indicating which attributes are of interest.
3524bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3525{
3526 int foundCount = 0;
3527 int matchingCount = 0;
59509217 3528
24777478
JS
3529 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3530 while (node)
59509217 3531 {
24777478 3532 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3533 // wxASSERT (para != NULL);
59509217 3534
24777478 3535 if (para)
59509217 3536 {
24777478
JS
3537 // Stop searching if we're beyond the range of interest
3538 if (para->GetRange().GetStart() > range.GetEnd())
3539 return foundCount == matchingCount && foundCount != 0;
59509217 3540
24777478 3541 if (!para->GetRange().IsOutside(range))
59509217 3542 {
24777478 3543 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3544
24777478
JS
3545 while (node2)
3546 {
3547 wxRichTextObject* child = node2->GetData();
3548 // Allow for empty string if no buffer
3549 wxRichTextRange childRange = child->GetRange();
3550 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3551 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3552
345c78ca 3553 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
24777478
JS
3554 {
3555 foundCount ++;
3556 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3557
32423dd8 3558 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
24777478
JS
3559 matchingCount ++;
3560 }
59509217 3561
24777478
JS
3562 node2 = node2->GetNext();
3563 }
59509217
JS
3564 }
3565 }
59509217 3566
24777478 3567 node = node->GetNext();
59509217
JS
3568 }
3569
24777478
JS
3570 return foundCount == matchingCount && foundCount != 0;
3571}
59509217 3572
24777478
JS
3573/// Test if this whole range has paragraph attributes of the specified kind. If any
3574/// of the attributes are different within the range, the test fails. You
3575/// can use this to implement, for example, centering button updating. style must have
3576/// flags indicating which attributes are of interest.
3577bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3578{
3579 int foundCount = 0;
3580 int matchingCount = 0;
3581
3582 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3583 while (node)
38f833b1 3584 {
24777478 3585 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3586 // wxASSERT (para != NULL);
24777478
JS
3587
3588 if (para)
38f833b1 3589 {
24777478
JS
3590 // Stop searching if we're beyond the range of interest
3591 if (para->GetRange().GetStart() > range.GetEnd())
3592 return foundCount == matchingCount && foundCount != 0;
3593
3594 if (!para->GetRange().IsOutside(range))
38f833b1 3595 {
24777478
JS
3596 wxRichTextAttr textAttr = GetAttributes();
3597 // Apply the paragraph style
3598 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3599
3600 foundCount ++;
32423dd8 3601 if (textAttr.EqPartial(style, false /* strong test */))
24777478 3602 matchingCount ++;
38f833b1
JS
3603 }
3604 }
24777478
JS
3605
3606 node = node->GetNext();
38f833b1 3607 }
24777478
JS
3608 return foundCount == matchingCount && foundCount != 0;
3609}
5d7836c4 3610
cc2aecde
JS
3611void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3612{
3613 wxRichTextBuffer* buffer = GetBuffer();
3614 if (buffer && buffer->GetRichTextCtrl())
3615 buffer->GetRichTextCtrl()->PrepareContent(container);
3616}
3617
590a0f8b
JS
3618/// Set character or paragraph properties
3619bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3620{
3621 wxRichTextBuffer* buffer = GetBuffer();
3622
3623 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3624 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3625 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3626 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3627 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3628
3629 // If we are associated with a control, make undoable; otherwise, apply immediately
3630 // to the data.
3631
3632 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3633
3634 wxRichTextAction* action = NULL;
3635
3636 if (haveControl && withUndo)
3637 {
3638 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3639 action->SetRange(range);
3640 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3641 }
3642
3643 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3644 while (node)
3645 {
3646 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3647 // wxASSERT (para != NULL);
3648
3649 if (para && para->GetChildCount() > 0)
3650 {
3651 // Stop searching if we're beyond the range of interest
3652 if (para->GetRange().GetStart() > range.GetEnd())
3653 break;
3654
3655 if (!para->GetRange().IsOutside(range))
3656 {
3657 // We'll be using a copy of the paragraph to make style changes,
3658 // not updating the buffer directly.
3659 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3660
3661 if (haveControl && withUndo)
3662 {
3663 newPara = new wxRichTextParagraph(*para);
3664 action->GetNewParagraphs().AppendChild(newPara);
3665
3666 // Also store the old ones for Undo
3667 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3668 }
3669 else
3670 newPara = para;
3671
3672 if (parasOnly)
3673 {
3674 if (removeProperties)
3675 {
3676 // Removes the given style from the paragraph
3677 // TODO
3678 newPara->GetProperties().RemoveProperties(properties);
3679 }
3680 else if (resetExistingProperties)
3681 newPara->GetProperties() = properties;
3682 else
3683 newPara->GetProperties().MergeProperties(properties);
3684 }
3685
3686 // When applying paragraph styles dynamically, don't change the text objects' attributes
3687 // since they will computed as needed. Only apply the character styling if it's _only_
3688 // character styling. This policy is subject to change and might be put under user control.
3689
3690 // Hm. we might well be applying a mix of paragraph and character styles, in which
3691 // case we _do_ want to apply character styles regardless of what para styles are set.
3692 // But if we're applying a paragraph style, which has some character attributes, but
3693 // we only want the paragraphs to hold this character style, then we _don't_ want to
3694 // apply the character style. So we need to be able to choose.
3695
3696 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3697 {
3698 wxRichTextRange childRange(range);
3699 childRange.LimitTo(newPara->GetRange());
3700
3701 // Find the starting position and if necessary split it so
3702 // we can start applying different properties.
3703 // TODO: check that the properties actually change or are different
3704 // from properties outside of range
3705 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3706 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3707
3708 if (childRange.GetStart() == newPara->GetRange().GetStart())
3709 firstObject = newPara->GetChildren().GetFirst()->GetData();
3710 else
3711 firstObject = newPara->SplitAt(range.GetStart());
3712
3713 // Increment by 1 because we're apply the style one _after_ the split point
3714 long splitPoint = childRange.GetEnd();
3715 if (splitPoint != newPara->GetRange().GetEnd())
3716 splitPoint ++;
3717
3718 // Find last object
3719 if (splitPoint == newPara->GetRange().GetEnd())
3720 lastObject = newPara->GetChildren().GetLast()->GetData();
3721 else
3722 // lastObject is set as a side-effect of splitting. It's
3723 // returned as the object before the new object.
3724 (void) newPara->SplitAt(splitPoint, & lastObject);
3725
3726 wxASSERT(firstObject != NULL);
3727 wxASSERT(lastObject != NULL);
3728
3729 if (!firstObject || !lastObject)
3730 continue;
3731
3732 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3733 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3734
3735 wxASSERT(firstNode);
3736 wxASSERT(lastNode);
3737
3738 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3739
3740 while (node2)
3741 {
3742 wxRichTextObject* child = node2->GetData();
3743
3744 if (removeProperties)
3745 {
3746 // Removes the given properties from the paragraph
3747 child->GetProperties().RemoveProperties(properties);
3748 }
3749 else if (resetExistingProperties)
3750 child->GetProperties() = properties;
3751 else
3752 {
3753 child->GetProperties().MergeProperties(properties);
3754 }
3755
3756 if (node2 == lastNode)
3757 break;
3758
3759 node2 = node2->GetNext();
3760 }
3761 }
3762 }
3763 }
3764
3765 node = node->GetNext();
3766 }
3767
3768 // Do action, or delay it until end of batch.
3769 if (haveControl && withUndo)
3770 buffer->SubmitAction(action);
3771
3772 return true;
3773}
3774
5d7836c4
JS
3775void wxRichTextParagraphLayoutBox::Reset()
3776{
3777 Clear();
3778
603f702b
JS
3779 wxRichTextBuffer* buffer = GetBuffer();
3780 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3781 {
603f702b
JS
3782 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3783 event.SetEventObject(buffer->GetRichTextCtrl());
3784 event.SetContainer(this);
cd8ba0d9
JS
3785
3786 buffer->SendEvent(event, true);
3787 }
3788
7fe8059f 3789 AddParagraph(wxEmptyString);
3e541562 3790
cc2aecde
JS
3791 PrepareContent(*this);
3792
603f702b 3793 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3794}
3795
38113684
JS
3796/// Invalidate the buffer. With no argument, invalidates whole buffer.
3797void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3798{
603f702b 3799 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3800
603f702b
JS
3801 DoInvalidate(invalidRange);
3802}
3803
3804// Do the (in)validation for this object only
3805void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3806{
1e967276 3807 if (invalidRange == wxRICHTEXT_ALL)
38113684 3808 {
1e967276 3809 m_invalidRange = wxRICHTEXT_ALL;
38113684 3810 }
1e967276 3811 // Already invalidating everything
603f702b
JS
3812 else if (m_invalidRange == wxRICHTEXT_ALL)
3813 {
3814 }
3815 else
3816 {
3817 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3818 m_invalidRange.SetStart(invalidRange.GetStart());
3819 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3820 m_invalidRange.SetEnd(invalidRange.GetEnd());
3821 }
3822}
39a1c2f2 3823
603f702b
JS
3824// Do the (in)validation both up and down the hierarchy
3825void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3826{
3827 Invalidate(invalidRange);
3828
3829 if (invalidRange != wxRICHTEXT_NONE)
3830 {
3831 // Now go up the hierarchy
3832 wxRichTextObject* thisObj = this;
3833 wxRichTextObject* p = GetParent();
3834 while (p)
3835 {
3836 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3837 if (l)
3838 l->DoInvalidate(thisObj->GetRange());
3839
3840 thisObj = p;
3841 p = p->GetParent();
3842 }
3843 }
38113684
JS
3844}
3845
3846/// Get invalid range, rounding to entire paragraphs if argument is true.
3847wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3848{
1e967276 3849 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3850 return m_invalidRange;
39a1c2f2 3851
38113684 3852 wxRichTextRange range = m_invalidRange;
39a1c2f2 3853
38113684
JS
3854 if (wholeParagraphs)
3855 {
3856 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3857 if (para1)
3858 range.SetStart(para1->GetRange().GetStart());
f7667b84
JS
3859
3860 // FIXME: be more intelligent about this. Check if we have floating objects
3861 // before the end of the range. But it's not clear how we can in general
3862 // tell where it's safe to stop laying out.
3863 // Anyway, this code is central to efficiency when laying in floating mode.
3864 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3865 {
3866 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
3867 if (para2)
3868 range.SetEnd(para2->GetRange().GetEnd());
3869 }
3870 else
3871 // Floating layout means that all children should be laid out,
3872 // because we can't tell how the whole buffer will be affected.
3873 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3874 }
3875 return range;
3876}
3877
fe5aa22c
JS
3878/// Apply the style sheet to the buffer, for example if the styles have changed.
3879bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3880{
3881 wxASSERT(styleSheet != NULL);
3882 if (!styleSheet)
3883 return false;
3884
3885 int foundCount = 0;
3886
44580804
JS
3887 wxRichTextAttr attr(GetBasicStyle());
3888 if (GetBasicStyle().HasParagraphStyleName())
3889 {
3890 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3891 if (paraDef)
3892 {
3893 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3894 SetBasicStyle(attr);
3895 foundCount ++;
3896 }
3897 }
3898
3899 if (GetBasicStyle().HasCharacterStyleName())
3900 {
3901 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3902 if (charDef)
3903 {
3904 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3905 SetBasicStyle(attr);
3906 foundCount ++;
3907 }
3908 }
3909
fe5aa22c
JS
3910 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3911 while (node)
3912 {
3913 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3914 // wxASSERT (para != NULL);
fe5aa22c
JS
3915
3916 if (para)
3917 {
38f833b1
JS
3918 // Combine paragraph and list styles. If there is a list style in the original attributes,
3919 // the current indentation overrides anything else and is used to find the item indentation.
3920 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3921 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3922 // exception as above).
3923 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3924 // So when changing a list style interactively, could retrieve level based on current style, then
3925 // set appropriate indent and apply new style.
41a85215 3926
bbd55ff9
JS
3927 int outline = -1;
3928 int num = -1;
3929 if (para->GetAttributes().HasOutlineLevel())
3930 outline = para->GetAttributes().GetOutlineLevel();
3931 if (para->GetAttributes().HasBulletNumber())
3932 num = para->GetAttributes().GetBulletNumber();
3933
38f833b1
JS
3934 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3935 {
3936 int currentIndent = para->GetAttributes().GetLeftIndent();
3937
3938 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3939 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3940 if (paraDef && !listDef)
3941 {
336d8ae9 3942 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3943 foundCount ++;
3944 }
3945 else if (listDef && !paraDef)
3946 {
3947 // Set overall style defined for the list style definition
336d8ae9 3948 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3949
3950 // Apply the style for this level
3951 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3952 foundCount ++;
3953 }
3954 else if (listDef && paraDef)
3955 {
3956 // Combines overall list style, style for level, and paragraph style
336d8ae9 3957 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3958 foundCount ++;
3959 }
3960 }
3961 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3962 {
3963 int currentIndent = para->GetAttributes().GetLeftIndent();
3964
3965 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3966
41a85215 3967 // Overall list definition style
336d8ae9 3968 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3969
38f833b1
JS
3970 // Style for this level
3971 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3972
3973 foundCount ++;
3974 }
3975 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3976 {
3977 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3978 if (def)
3979 {
336d8ae9 3980 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3981 foundCount ++;
3982 }
3983 }
bbd55ff9
JS
3984
3985 if (outline != -1)
3986 para->GetAttributes().SetOutlineLevel(outline);
3987 if (num != -1)
3988 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3989 }
3990
3991 node = node->GetNext();
3992 }
3993 return foundCount != 0;
3994}
3995
38f833b1
JS
3996/// Set list style
3997bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3998{
603f702b
JS
3999 wxRichTextBuffer* buffer = GetBuffer();
4000 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 4001
38f833b1
JS
4002 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4003 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4004 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4005 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4006
38f833b1
JS
4007 // Current number, if numbering
4008 int n = startFrom;
41a85215 4009
38f833b1
JS
4010 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4011
4012 // If we are associated with a control, make undoable; otherwise, apply immediately
4013 // to the data.
4014
603f702b 4015 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4016
4017 wxRichTextAction* action = NULL;
4018
4019 if (haveControl && withUndo)
4020 {
603f702b 4021 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4022 action->SetRange(range);
603f702b 4023 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4024 }
4025
4026 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4027 while (node)
4028 {
4029 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4030 // wxASSERT (para != NULL);
38f833b1
JS
4031
4032 if (para && para->GetChildCount() > 0)
4033 {
4034 // Stop searching if we're beyond the range of interest
4035 if (para->GetRange().GetStart() > range.GetEnd())
4036 break;
4037
4038 if (!para->GetRange().IsOutside(range))
4039 {
4040 // We'll be using a copy of the paragraph to make style changes,
4041 // not updating the buffer directly.
4042 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4043
4044 if (haveControl && withUndo)
4045 {
4046 newPara = new wxRichTextParagraph(*para);
4047 action->GetNewParagraphs().AppendChild(newPara);
4048
4049 // Also store the old ones for Undo
4050 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4051 }
4052 else
4053 newPara = para;
41a85215 4054
38f833b1
JS
4055 if (def)
4056 {
4057 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4058 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 4059
38f833b1
JS
4060 // How is numbering going to work?
4061 // If we are renumbering, or numbering for the first time, we need to keep
4062 // track of the number for each level. But we might be simply applying a different
4063 // list style.
4064 // In Word, applying a style to several paragraphs, even if at different levels,
4065 // reverts the level back to the same one. So we could do the same here.
4066 // Renumbering will need to be done when we promote/demote a paragraph.
4067
4068 // Apply the overall list style, and item style for this level
24777478 4069 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4070 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4071
d2d0adc7 4072 // Now we need to do numbering
4ce3ebd3
JS
4073 // Preserve the existing list item continuation bullet style, if any
4074 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4075 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4076 else
38f833b1 4077 {
4ce3ebd3
JS
4078 if (renumber)
4079 {
4080 newPara->GetAttributes().SetBulletNumber(n);
4081 }
41a85215 4082
4ce3ebd3
JS
4083 n ++;
4084 }
38f833b1
JS
4085 }
4086 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4087 {
4088 // if def is NULL, remove list style, applying any associated paragraph style
4089 // to restore the attributes
4090
4091 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4092 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 4093 newPara->GetAttributes().SetBulletText(wxEmptyString);
c4168888 4094 newPara->GetAttributes().SetBulletStyle(0);
41a85215 4095
38f833b1 4096 // Eliminate the main list-related attributes
d2d0adc7 4097 newPara->GetAttributes().SetFlags(newPara->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT & ~wxTEXT_ATTR_BULLET_STYLE & ~wxTEXT_ATTR_BULLET_NUMBER & ~wxTEXT_ATTR_BULLET_TEXT & wxTEXT_ATTR_LIST_STYLE_NAME);
41a85215 4098
38f833b1
JS
4099 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4100 {
4101 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4102 if (def)
4103 {
336d8ae9 4104 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
4105 }
4106 }
4107 }
4108 }
4109 }
4110
4111 node = node->GetNext();
4112 }
4113
4114 // Do action, or delay it until end of batch.
4115 if (haveControl && withUndo)
603f702b 4116 buffer->SubmitAction(action);
38f833b1
JS
4117
4118 return true;
4119}
4120
4121bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4122{
603f702b
JS
4123 wxRichTextBuffer* buffer = GetBuffer();
4124 if (buffer && buffer->GetStyleSheet())
38f833b1 4125 {
603f702b 4126 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4127 if (def)
4128 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4129 }
4130 return false;
4131}
4132
4133/// Clear list for given range
4134bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4135{
4136 return SetListStyle(range, NULL, flags);
4137}
4138
4139/// Number/renumber any list elements in the given range
4140bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4141{
4142 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4143}
4144
4145/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4146bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4147 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4148{
603f702b
JS
4149 wxRichTextBuffer* buffer = GetBuffer();
4150 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4151
38f833b1
JS
4152 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4153 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 4154#if wxDEBUG_LEVEL
38f833b1 4155 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 4156#endif
38f833b1
JS
4157
4158 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4159
38f833b1
JS
4160 // Max number of levels
4161 const int maxLevels = 10;
41a85215 4162
38f833b1
JS
4163 // The level we're looking at now
4164 int currentLevel = -1;
41a85215 4165
38f833b1
JS
4166 // The item number for each level
4167 int levels[maxLevels];
4168 int i;
41a85215 4169
38f833b1
JS
4170 // Reset all numbering
4171 for (i = 0; i < maxLevels; i++)
4172 {
4173 if (startFrom != -1)
d2d0adc7 4174 levels[i] = startFrom-1;
38f833b1 4175 else if (renumber) // start again
d2d0adc7 4176 levels[i] = 0;
38f833b1
JS
4177 else
4178 levels[i] = -1; // start from the number we found, if any
4179 }
41a85215 4180
bb7bbd12 4181#if wxDEBUG_LEVEL
38f833b1 4182 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
bb7bbd12 4183#endif
38f833b1
JS
4184
4185 // If we are associated with a control, make undoable; otherwise, apply immediately
4186 // to the data.
4187
603f702b 4188 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4189
4190 wxRichTextAction* action = NULL;
4191
4192 if (haveControl && withUndo)
4193 {
603f702b 4194 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4195 action->SetRange(range);
603f702b 4196 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4197 }
4198
4199 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4200 while (node)
4201 {
4202 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4203 // wxASSERT (para != NULL);
38f833b1
JS
4204
4205 if (para && para->GetChildCount() > 0)
4206 {
4207 // Stop searching if we're beyond the range of interest
4208 if (para->GetRange().GetStart() > range.GetEnd())
4209 break;
4210
4211 if (!para->GetRange().IsOutside(range))
4212 {
4213 // We'll be using a copy of the paragraph to make style changes,
4214 // not updating the buffer directly.
4215 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4216
4217 if (haveControl && withUndo)
4218 {
4219 newPara = new wxRichTextParagraph(*para);
4220 action->GetNewParagraphs().AppendChild(newPara);
4221
4222 // Also store the old ones for Undo
4223 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4224 }
4225 else
4226 newPara = para;
41a85215 4227
38f833b1
JS
4228 wxRichTextListStyleDefinition* defToUse = def;
4229 if (!defToUse)
4230 {
336d8ae9
VZ
4231 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4232 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 4233 }
41a85215 4234
38f833b1
JS
4235 if (defToUse)
4236 {
4237 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4238 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4239
d2d0adc7
JS
4240 // If we've specified a level to apply to all, change the level.
4241 if (specifiedLevel != -1)
38f833b1 4242 thisLevel = specifiedLevel;
41a85215 4243
38f833b1
JS
4244 // Do promotion if specified
4245 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4246 {
4247 thisLevel = thisLevel - promoteBy;
4248 if (thisLevel < 0)
4249 thisLevel = 0;
4250 if (thisLevel > 9)
4251 thisLevel = 9;
4252 }
41a85215 4253
38f833b1 4254 // Apply the overall list style, and item style for this level
24777478 4255 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4256 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4257
4ce3ebd3
JS
4258 // Preserve the existing list item continuation bullet style, if any
4259 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4260 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4261
38f833b1 4262 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 4263
38f833b1
JS
4264 if (currentLevel == -1)
4265 currentLevel = thisLevel;
41a85215 4266
38f833b1
JS
4267 // Same level as before, do nothing except increment level's number afterwards
4268 if (currentLevel == thisLevel)
4269 {
4270 }
4271 // A deeper level: start renumbering all levels after current level
4272 else if (thisLevel > currentLevel)
4273 {
4274 for (i = currentLevel+1; i <= thisLevel; i++)
4275 {
d2d0adc7 4276 levels[i] = 0;
38f833b1
JS
4277 }
4278 currentLevel = thisLevel;
4279 }
4280 else if (thisLevel < currentLevel)
4281 {
4282 currentLevel = thisLevel;
41a85215 4283 }
38f833b1
JS
4284
4285 // Use the current numbering if -1 and we have a bullet number already
4286 if (levels[currentLevel] == -1)
4287 {
4288 if (newPara->GetAttributes().HasBulletNumber())
4289 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4290 else
4291 levels[currentLevel] = 1;
4292 }
d2d0adc7
JS
4293 else
4294 {
4ce3ebd3
JS
4295 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4296 levels[currentLevel] ++;
d2d0adc7 4297 }
41a85215 4298
38f833b1
JS
4299 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4300
d2d0adc7
JS
4301 // Create the bullet text if an outline list
4302 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4303 {
4304 wxString text;
4305 for (i = 0; i <= currentLevel; i++)
4306 {
4307 if (!text.IsEmpty())
4308 text += wxT(".");
4309 text += wxString::Format(wxT("%d"), levels[i]);
4310 }
4311 newPara->GetAttributes().SetBulletText(text);
4312 }
38f833b1
JS
4313 }
4314 }
4315 }
4316
4317 node = node->GetNext();
4318 }
4319
4320 // Do action, or delay it until end of batch.
4321 if (haveControl && withUndo)
603f702b 4322 buffer->SubmitAction(action);
38f833b1
JS
4323
4324 return true;
4325}
4326
4327bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4328{
603f702b
JS
4329 wxRichTextBuffer* buffer = GetBuffer();
4330 if (buffer->GetStyleSheet())
38f833b1
JS
4331 {
4332 wxRichTextListStyleDefinition* def = NULL;
4333 if (!defName.IsEmpty())
603f702b 4334 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4335 return NumberList(range, def, flags, startFrom, specifiedLevel);
4336 }
4337 return false;
4338}
4339
4340/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4341bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4342{
4343 // TODO
4344 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4345 // to NumberList with a flag indicating promotion is required within one of the ranges.
4346 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4347 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4348 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4349 // list position will start from 1.
4350 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4351 // We can end the renumbering at this point.
41a85215 4352
38f833b1 4353 // For now, only renumber within the promotion range.
41a85215 4354
38f833b1
JS
4355 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4356}
4357
4358bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4359{
603f702b
JS
4360 wxRichTextBuffer* buffer = GetBuffer();
4361 if (buffer->GetStyleSheet())
38f833b1
JS
4362 {
4363 wxRichTextListStyleDefinition* def = NULL;
4364 if (!defName.IsEmpty())
603f702b 4365 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4366 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4367 }
4368 return false;
4369}
4370
d2d0adc7
JS
4371/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4372/// position of the paragraph that it had to start looking from.
24777478 4373bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 4374{
c4168888 4375 // TODO: add GetNextChild/GetPreviousChild to composite
4ce3ebd3
JS
4376 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4377 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4378 {
4379 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4380 if (node)
4381 {
4382 node = node->GetPrevious();
4383 if (node)
4384 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4385 else
4386 previousParagraph = NULL;
4387 }
4388 else
4389 previousParagraph = NULL;
4390 }
4391
c4168888 4392 if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
d2d0adc7 4393 return false;
3e541562 4394
603f702b
JS
4395 wxRichTextBuffer* buffer = GetBuffer();
4396 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4397 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 4398 {
336d8ae9 4399 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
4400 if (def)
4401 {
4402 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4403 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4404
d2d0adc7
JS
4405 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4406
4407 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4408 if (previousParagraph->GetAttributes().HasBulletName())
4409 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4410 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4411 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4412
d2d0adc7
JS
4413 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4414 attr.SetBulletNumber(nextNumber);
3e541562 4415
d2d0adc7
JS
4416 if (isOutline)
4417 {
4418 wxString text = previousParagraph->GetAttributes().GetBulletText();
4419 if (!text.IsEmpty())
4420 {
4421 int pos = text.Find(wxT('.'), true);
4422 if (pos != wxNOT_FOUND)
4423 {
4424 text = text.Mid(0, text.Length() - pos - 1);
4425 }
4426 else
4427 text = wxEmptyString;
4428 if (!text.IsEmpty())
4429 text += wxT(".");
4430 text += wxString::Format(wxT("%d"), nextNumber);
4431 attr.SetBulletText(text);
4432 }
4433 }
3e541562 4434
d2d0adc7
JS
4435 return true;
4436 }
4437 else
4438 return false;
4439 }
4440 else
4441 return false;
4442}
4443
5d7836c4
JS
4444/*!
4445 * wxRichTextParagraph
4446 * This object represents a single paragraph (or in a straight text editor, a line).
4447 */
4448
603f702b 4449IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4450
cfa3b256
JS
4451wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4452
24777478 4453wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4454 wxRichTextCompositeObject(parent)
5d7836c4 4455{
5d7836c4
JS
4456 if (style)
4457 SetAttributes(*style);
4458}
4459
24777478 4460wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4461 wxRichTextCompositeObject(parent)
5d7836c4 4462{
4f32b3cf
JS
4463 if (paraStyle)
4464 SetAttributes(*paraStyle);
5d7836c4 4465
4f32b3cf 4466 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4467}
4468
4469wxRichTextParagraph::~wxRichTextParagraph()
4470{
4471 ClearLines();
4472}
4473
4474/// Draw the item
8db2e3ef 4475bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4476{
603f702b
JS
4477 if (!IsShown())
4478 return true;
4479
4480 // Currently we don't merge these attributes with the parent, but we
4481 // should consider whether we should (e.g. if we set a border colour
4482 // for all paragraphs). But generally box attributes are likely to be
4483 // different for different objects.
4484 wxRect paraRect = GetRect();
24777478 4485 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef
JS
4486 context.ApplyVirtualAttributes(attr, this);
4487
4488 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
fe5aa22c 4489
5d7836c4 4490 // Draw the bullet, if any
4ce3ebd3 4491 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
5d7836c4 4492 {
fe5aa22c 4493 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4494 {
fe5aa22c 4495 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4496 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4497
8db2e3ef 4498 wxRichTextAttr bulletAttr(attr);
d2d0adc7 4499
e3eac0ff
JS
4500 // Combine with the font of the first piece of content, if one is specified
4501 if (GetChildren().GetCount() > 0)
4502 {
4503 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4504 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4505 {
4506 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4507 }
4508 }
4509
d2d0adc7 4510 // Get line height from first line, if any
d3b9f782 4511 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4512
4513 wxPoint linePos;
4514 int lineHeight wxDUMMY_INITIALIZE(0);
4515 if (line)
5d7836c4 4516 {
d2d0adc7
JS
4517 lineHeight = line->GetSize().y;
4518 linePos = line->GetPosition() + GetPosition();
5d7836c4 4519 }
d2d0adc7 4520 else
f089713f 4521 {
f089713f 4522 wxFont font;
44cc96a8
JS
4523 if (bulletAttr.HasFont() && GetBuffer())
4524 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4525 else
4526 font = (*wxNORMAL_FONT);
4527
ecb5fbf1 4528 wxCheckSetFont(dc, font);
f089713f 4529
d2d0adc7
JS
4530 lineHeight = dc.GetCharHeight();
4531 linePos = GetPosition();
4532 linePos.y += spaceBeforePara;
4533 }
f089713f 4534
d2d0adc7 4535 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4536
d2d0adc7
JS
4537 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4538 {
4539 if (wxRichTextBuffer::GetRenderer())
4540 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4541 }
3e541562
JS
4542 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4543 {
d2d0adc7
JS
4544 if (wxRichTextBuffer::GetRenderer())
4545 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4546 }
5d7836c4
JS
4547 else
4548 {
4549 wxString bulletText = GetBulletText();
3e541562 4550
d2d0adc7
JS
4551 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4552 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4553 }
4554 }
4555 }
7fe8059f 4556
5d7836c4
JS
4557 // Draw the range for each line, one object at a time.
4558
4559 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4560 while (node)
4561 {
4562 wxRichTextLine* line = node->GetData();
1e967276 4563 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4564
5d7836c4
JS
4565 // Lines are specified relative to the paragraph
4566
4567 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4568
7051fa41
JS
4569 // Don't draw if off the screen
4570 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4571 {
7051fa41
JS
4572 wxPoint objectPosition = linePosition;
4573 int maxDescent = line->GetDescent();
4574
4575 // Loop through objects until we get to the one within range
4576 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4577
7051fa41
JS
4578 int i = 0;
4579 while (node2)
5d7836c4 4580 {
7051fa41 4581 wxRichTextObject* child = node2->GetData();
5d7836c4 4582
e12b91a3 4583 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4584 {
7051fa41
JS
4585 // Draw this part of the line at the correct position
4586 wxRichTextRange objectRange(child->GetRange());
4587 objectRange.LimitTo(lineRange);
4588
4589 wxSize objectSize;
603f702b 4590 if (child->IsTopLevel())
7051fa41 4591 {
603f702b
JS
4592 objectSize = child->GetCachedSize();
4593 objectRange = child->GetOwnRange();
7051fa41
JS
4594 }
4595 else
7051fa41 4596 {
603f702b
JS
4597#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4598 if (i < (int) line->GetObjectSizes().GetCount())
4599 {
4600 objectSize.x = line->GetObjectSizes()[(size_t) i];
4601 }
4602 else
4603#endif
4604 {
4605 int descent = 0;
8db2e3ef 4606 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
603f702b 4607 }
7051fa41 4608 }
5d7836c4 4609
7051fa41
JS
4610 // Use the child object's width, but the whole line's height
4611 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
8db2e3ef 4612 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4613
7051fa41
JS
4614 objectPosition.x += objectSize.x;
4615 i ++;
4616 }
4617 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4618 // Can break out of inner loop now since we've passed this line's range
4619 break;
5d7836c4 4620
7051fa41
JS
4621 node2 = node2->GetNext();
4622 }
5d7836c4
JS
4623 }
4624
4625 node = node->GetNext();
7fe8059f 4626 }
5d7836c4
JS
4627
4628 return true;
4629}
4630
4f3d5bc0
JS
4631// Get the range width using partial extents calculated for the whole paragraph.
4632static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4633{
4634 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4635
affbfa1f
JS
4636 if (partialExtents.GetCount() < (size_t) range.GetLength())
4637 return 0;
4638
4f3d5bc0
JS
4639 int leftMostPos = 0;
4640 if (range.GetStart() - para.GetRange().GetStart() > 0)
4641 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4642
4643 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4644
4645 int w = rightMostPos - leftMostPos;
4646
4647 return w;
4648}
4649
5d7836c4 4650/// Lay the item out
8db2e3ef 4651bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 4652{
cdaed652
VZ
4653 // Deal with floating objects firstly before the normal layout
4654 wxRichTextBuffer* buffer = GetBuffer();
4655 wxASSERT(buffer);
e12b91a3 4656
07d4142f 4657 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
e12b91a3
JS
4658
4659 if (wxRichTextBuffer::GetFloatingLayoutMode())
4660 {
4661 wxASSERT(collector != NULL);
4662 if (collector)
4663 LayoutFloat(dc, context, rect, parentRect, style, collector);
4664 }
cdaed652 4665
24777478 4666 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef 4667 context.ApplyVirtualAttributes(attr, this);
fe5aa22c 4668
169adfa9
JS
4669 // ClearLines();
4670
5d7836c4 4671 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4672 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4673 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4674 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4675 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4676 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4677
4678 int lineSpacing = 0;
4679
4680 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
77120d82 4681 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.HasFont())
5d7836c4 4682 {
77120d82
JS
4683 wxFont font(buffer->GetFontTable().FindFont(attr));
4684 if (font.IsOk())
4685 {
4686 wxCheckSetFont(dc, font);
4687 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4688 }
5d7836c4
JS
4689 }
4690
5d7836c4
JS
4691 // Start position for each line relative to the paragraph
4692 int startPositionFirstLine = leftIndent;
4693 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4694
4695 // If we have a bullet in this paragraph, the start position for the first line's text
4696 // is actually leftIndent + leftSubIndent.
fe5aa22c 4697 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4698 startPositionFirstLine = startPositionSubsequentLines;
4699
5d7836c4
JS
4700 long lastEndPos = GetRange().GetStart()-1;
4701 long lastCompletedEndPos = lastEndPos;
4702
4703 int currentWidth = 0;
4704 SetPosition(rect.GetPosition());
4705
4706 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4707 int lineHeight = 0;
4708 int maxWidth = 0;
603f702b 4709 int maxHeight = currentPosition.y;
476a319a 4710 int maxAscent = 0;
5d7836c4 4711 int maxDescent = 0;
5d7836c4 4712 int lineCount = 0;
cdaed652
VZ
4713 int lineAscent = 0;
4714 int lineDescent = 0;
5d7836c4 4715
2f45f554
JS
4716 wxRichTextObjectList::compatibility_iterator node;
4717
4718#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4719 wxUnusedVar(style);
4720 wxArrayInt partialExtents;
4721
4722 wxSize paraSize;
8aab23a1 4723 int paraDescent = 0;
2f45f554
JS
4724
4725 // This calculates the partial text extents
914a4e23 4726 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents);
2f45f554
JS
4727#else
4728 node = m_children.GetFirst();
ecb5fbf1
JS
4729 while (node)
4730 {
4731 wxRichTextObject* child = node->GetData();
4732
603f702b 4733 //child->SetCachedSize(wxDefaultSize);
8db2e3ef 4734 child->Layout(dc, context, rect, style);
ecb5fbf1
JS
4735
4736 node = node->GetNext();
4737 }
31778480
JS
4738#endif
4739
5d7836c4
JS
4740 // Split up lines
4741
4742 // We may need to go back to a previous child, in which case create the new line,
4743 // find the child corresponding to the start position of the string, and
4744 // continue.
4745
603f702b
JS
4746 wxRect availableRect;
4747
ecb5fbf1 4748 node = m_children.GetFirst();
5d7836c4
JS
4749 while (node)
4750 {
4751 wxRichTextObject* child = node->GetData();
4752
cdaed652 4753 // If floating, ignore. We already laid out floats.
603f702b
JS
4754 // Also ignore if empty object, except if we haven't got any
4755 // size yet.
e12b91a3
JS
4756 if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4757 || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
603f702b 4758 )
affbfa1f
JS
4759 {
4760 node = node->GetNext();
4761 continue;
4762 }
4763
5d7836c4
JS
4764 // If this is e.g. a composite text box, it will need to be laid out itself.
4765 // But if just a text fragment or image, for example, this will
4766 // do nothing. NB: won't we need to set the position after layout?
4767 // since for example if position is dependent on vertical line size, we
4768 // can't tell the position until the size is determined. So possibly introduce
4769 // another layout phase.
4770
5d7836c4
JS
4771 // We may only be looking at part of a child, if we searched back for wrapping
4772 // and found a suitable point some way into the child. So get the size for the fragment
4773 // if necessary.
3e541562 4774
ff76711f
JS
4775 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4776 long lastPosToUse = child->GetRange().GetEnd();
4777 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4778
ff76711f
JS
4779 if (lineBreakInThisObject)
4780 lastPosToUse = nextBreakPos;
5d7836c4
JS
4781
4782 wxSize childSize;
4783 int childDescent = 0;
3e541562 4784
603f702b
JS
4785 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4786 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4787 rect.width - startOffset - rightIndent, rect.height);
4788
4789 if (child->IsTopLevel())
4790 {
4791 wxSize oldSize = child->GetCachedSize();
4792
4793 child->Invalidate(wxRICHTEXT_ALL);
4794 child->SetPosition(wxPoint(0, 0));
4795
4796 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4797 // lays out the object again using the minimum size
4798 // The position will be determined by its location in its line,
4799 // and not by the child's actual position.
8db2e3ef
JS
4800 child->LayoutToBestSize(dc, context, buffer,
4801 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4802
4803 if (oldSize != child->GetCachedSize())
4804 {
4805 partialExtents.Clear();
4806
4807 // Recalculate the partial text extents since the child object changed size
914a4e23 4808 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
603f702b
JS
4809 }
4810 }
4811
4812 // Problem: we need to layout composites here for which we need the available width,
4813 // but we can't get the available width without using the float collector which
4814 // needs to know the object height.
4815
ff76711f 4816 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4817 {
4818 childSize = child->GetCachedSize();
4819 childDescent = child->GetDescent();
4820 }
4821 else
4f3d5bc0
JS
4822 {
4823#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4824 // Get height only, then the width using the partial extents
914a4e23 4825 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4f3d5bc0
JS
4826 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4827#else
914a4e23 4828 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize());
4f3d5bc0
JS
4829#endif
4830 }
ff76711f 4831
603f702b
JS
4832 bool doLoop = true;
4833 int loopIterations = 0;
4834
4835 // If there are nested objects that need to lay themselves out, we have to do this in a
4836 // loop because the height of the object may well depend on the available width.
4837 // And because of floating object positioning, the available width depends on the
4838 // height of the object and whether it will clash with the floating objects.
4839 // So, we see whether the available width changes due to the presence of floating images.
4840 // If it does, then we'll use the new restricted width to find the object height again.
4841 // If this causes another restriction in the available width, we'll try again, until
4842 // either we lose patience or the available width settles down.
4843 do
4844 {
4845 loopIterations ++;
4846
4847 wxRect oldAvailableRect = availableRect;
4848
4849 // Available width depends on the floating objects and the line height.
7c9fdebe 4850 // Note: the floating objects may be placed vertically along the two sides of
603f702b
JS
4851 // buffer, so we may have different available line widths with different
4852 // [startY, endY]. So, we can't determine how wide the available
4853 // space is until we know the exact line height.
a70eb13e
JS
4854 if (childDescent == 0)
4855 {
4856 lineHeight = wxMax(lineHeight, childSize.y);
4857 lineDescent = maxDescent;
4858 lineAscent = maxAscent;
4859 }
4860 else
4861 {
4862 lineDescent = wxMax(childDescent, maxDescent);
4863 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4864 }
4865 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
603f702b 4866
e12b91a3 4867 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
603f702b 4868 {
e12b91a3
JS
4869 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4870
4871 // Adjust availableRect to the space that is available when taking floating objects into account.
4872
4873 if (floatAvailableRect.x + startOffset > availableRect.x)
4874 {
4875 int newX = floatAvailableRect.x + startOffset;
4876 int newW = availableRect.width - (newX - availableRect.x);
4877 availableRect.x = newX;
4878 availableRect.width = newW;
4879 }
603f702b 4880
e12b91a3
JS
4881 if (floatAvailableRect.width < availableRect.width)
4882 availableRect.width = floatAvailableRect.width;
4883 }
603f702b
JS
4884
4885 currentPosition.x = availableRect.x - rect.x;
4886
4887 if (child->IsTopLevel() && loopIterations <= 20)
4888 {
4889 if (availableRect != oldAvailableRect)
4890 {
4891 wxSize oldSize = child->GetCachedSize();
4892
603f702b
JS
4893 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4894 // lays out the object again using the minimum size
4895 child->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 4896 child->LayoutToBestSize(dc, context, buffer,
914a4e23 4897 attr, child->GetAttributes(), availableRect, parentRect.GetSize(), style);
603f702b
JS
4898 childSize = child->GetCachedSize();
4899 childDescent = child->GetDescent();
603f702b
JS
4900
4901 if (oldSize != child->GetCachedSize())
4902 {
4903 partialExtents.Clear();
4904
4905 // Recalculate the partial text extents since the child object changed size
914a4e23 4906 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
603f702b 4907 }
cdaed652 4908
603f702b
JS
4909 // Go around the loop finding the available rect for the given floating objects
4910 }
4911 else
4912 doLoop = false;
4913 }
4914 else
4915 doLoop = false;
4916 }
4917 while (doLoop);
cdaed652 4918
20d09da5
JS
4919 if (child->IsTopLevel())
4920 {
4921 // We can move it to the correct position at this point
32423dd8
JS
4922 // TODO: probably need to add margin
4923 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
20d09da5
JS
4924 }
4925
ff76711f
JS
4926 // Cases:
4927 // 1) There was a line break BEFORE the natural break
4928 // 2) There was a line break AFTER the natural break
603f702b
JS
4929 // 3) It's the last line
4930 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4931
603f702b
JS
4932 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4933 ||
4934 (childSize.x + currentWidth > availableRect.width)
914a4e23 4935#if 0
603f702b
JS
4936 ||
4937 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
914a4e23 4938#endif
603f702b 4939 )
5d7836c4
JS
4940 {
4941 long wrapPosition = 0;
603f702b
JS
4942 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4943 wrapPosition = child->GetRange().GetEnd();
4944 else
5d7836c4
JS
4945
4946 // Find a place to wrap. This may walk back to previous children,
4947 // for example if a word spans several objects.
cdaed652
VZ
4948 // Note: one object must contains only one wxTextAtrr, so the line height will not
4949 // change inside one object. Thus, we can pass the remain line width to the
4950 // FindWrapPosition function.
8db2e3ef 4951 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4952 {
4953 // If the function failed, just cut it off at the end of this child.
4954 wrapPosition = child->GetRange().GetEnd();
4955 }
4956
4957 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4958 if (wrapPosition <= lastCompletedEndPos)
4959 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4960
603f702b
JS
4961 // Line end position shouldn't be the same as the end, or greater.
4962 if (wrapPosition >= GetRange().GetEnd())
a8a15de6 4963 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
603f702b 4964
5d7836c4 4965 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4966
5d7836c4
JS
4967 // Let's find the actual size of the current line now
4968 wxSize actualSize;
4969 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4970
a70eb13e 4971 childDescent = 0;
4ab8a5e2 4972
4f3d5bc0 4973#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4974 if (!child->IsEmpty())
4975 {
4976 // Get height only, then the width using the partial extents
914a4e23 4977 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
603f702b
JS
4978 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4979 }
4980 else
4f3d5bc0 4981#endif
914a4e23 4982 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
4f3d5bc0 4983
5d7836c4 4984 currentWidth = actualSize.x;
a70eb13e
JS
4985
4986 // The descent for the whole line at this point, is the correct max descent
4987 maxDescent = childDescent;
4988 // Maximum ascent
4989 maxAscent = actualSize.y-childDescent;
4990
4991 // lineHeight is given by the height for the whole line, since it will
4992 // take into account ascend/descend.
4993 lineHeight = actualSize.y;
7fe8059f 4994
07d4142f 4995 if (lineHeight == 0 && buffer)
603f702b 4996 {
07d4142f 4997 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
4998 wxCheckSetFont(dc, font);
4999 lineHeight = dc.GetCharHeight();
5000 }
5001
5002 if (maxDescent == 0)
5003 {
5004 int w, h;
5005 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5006 }
5007
5d7836c4 5008 // Add a new line
1e967276 5009 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 5010
1e967276
JS
5011 // Set relative range so we won't have to change line ranges when paragraphs are moved
5012 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
5013 line->SetPosition(currentPosition);
5014 line->SetSize(wxSize(currentWidth, lineHeight));
5015 line->SetDescent(maxDescent);
5016
603f702b
JS
5017 maxHeight = currentPosition.y + lineHeight;
5018
5d7836c4
JS
5019 // Now move down a line. TODO: add margins, spacing
5020 currentPosition.y += lineHeight;
5021 currentPosition.y += lineSpacing;
5d7836c4 5022 maxDescent = 0;
476a319a 5023 maxAscent = 0;
603f702b
JS
5024 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5025 currentWidth = 0;
7fe8059f 5026
5d7836c4
JS
5027 lineCount ++;
5028
a70eb13e 5029 // TODO: account for zero-length objects
603f702b 5030 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 5031
5d7836c4
JS
5032 lastEndPos = wrapPosition;
5033 lastCompletedEndPos = lastEndPos;
5034
5035 lineHeight = 0;
5036
603f702b
JS
5037 if (wrapPosition < GetRange().GetEnd()-1)
5038 {
5039 // May need to set the node back to a previous one, due to searching back in wrapping
5040 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5041 if (childAfterWrapPosition)
5042 node = m_children.Find(childAfterWrapPosition);
5043 else
5044 node = node->GetNext();
5045 }
5d7836c4
JS
5046 else
5047 node = node->GetNext();
603f702b
JS
5048
5049 // Apply paragraph styles such as alignment to the wrapped line
5050 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
5051 }
5052 else
5053 {
5054 // We still fit, so don't add a line, and keep going
5055 currentWidth += childSize.x;
a70eb13e
JS
5056
5057 if (childDescent == 0)
5058 {
5059 // An object with a zero descend value wants to take up the whole
5060 // height regardless of baseline
5061 lineHeight = wxMax(lineHeight, childSize.y);
5062 }
5063 else
5064 {
5065 maxDescent = wxMax(childDescent, maxDescent);
5066 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5067 }
5068
5069 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5d7836c4 5070
603f702b 5071 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
5072 lastEndPos = child->GetRange().GetEnd();
5073
5074 node = node->GetNext();
5075 }
5076 }
5077
07d4142f 5078 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 5079
914a4e23
JS
5080 // Add the last line - it's the current pos -> last para pos
5081 // Substract -1 because the last position is always the end-paragraph position.
5082 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
5083 {
5084 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
5085
5086 wxRichTextLine* line = AllocateLine(lineCount);
5087
5088 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
5089
5090 // Set relative range so we won't have to change line ranges when paragraphs are moved
5091 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5092
5093 line->SetPosition(currentPosition);
5094
5095 if (lineHeight == 0 && buffer)
5096 {
5097 wxFont font(buffer->GetFontTable().FindFont(attr));
5098 wxCheckSetFont(dc, font);
5099 lineHeight = dc.GetCharHeight();
5100 }
5101
5102 if (maxDescent == 0)
5103 {
5104 int w, h;
5105 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5106 }
5107
5108 line->SetSize(wxSize(currentWidth, lineHeight));
5109 line->SetDescent(maxDescent);
5110 currentPosition.y += lineHeight;
5111 currentPosition.y += lineSpacing;
5112 lineCount ++;
5113 }
5114
1e967276
JS
5115 // Remove remaining unused line objects, if any
5116 ClearUnusedLines(lineCount);
5117
603f702b
JS
5118 // We need to add back the margins etc.
5119 {
5120 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5121 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5122 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5123 SetCachedSize(marginRect.GetSize());
5124 }
5125
5126 // The maximum size is the length of the paragraph stretched out into a line.
5127 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5128 // this size. TODO: take into account line breaks.
5129 {
5130 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 5131 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 5132 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5133 SetMaxSize(marginRect.GetSize());
5134 }
5135
5136 // Find the greatest minimum size. Currently we only look at non-text objects,
5137 // which isn't ideal but it would be slow to find the maximum word width to
5138 // use as the minimum.
5139 {
5140 int minWidth = 0;
5141 node = m_children.GetFirst();
5142 while (node)
5143 {
5144 wxRichTextObject* child = node->GetData();
5145
5146 // If floating, ignore. We already laid out floats.
5147 // Also ignore if empty object, except if we haven't got any
5148 // size yet.
e12b91a3 5149 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
603f702b
JS
5150 {
5151 if (child->GetCachedSize().x > minWidth)
5152 minWidth = child->GetMinSize().x;
5153 }
5154 node = node->GetNext();
5155 }
5d7836c4 5156
603f702b
JS
5157 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5158 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5159 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5160 SetMinSize(marginRect.GetSize());
5161 }
5d7836c4 5162
2f45f554
JS
5163#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5164#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5165 // Use the text extents to calculate the size of each fragment in each line
5166 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5167 while (lineNode)
5168 {
5169 wxRichTextLine* line = lineNode->GetData();
5170 wxRichTextRange lineRange = line->GetAbsoluteRange();
5171
5172 // Loop through objects until we get to the one within range
5173 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5174
5175 while (node2)
5176 {
5177 wxRichTextObject* child = node2->GetData();
5178
affbfa1f 5179 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
5180 {
5181 wxRichTextRange rangeToUse = lineRange;
5182 rangeToUse.LimitTo(child->GetRange());
5183
5184 // Find the size of the child from the text extents, and store in an array
5185 // for drawing later
5186 int left = 0;
5187 if (rangeToUse.GetStart() > GetRange().GetStart())
5188 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5189 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5190 int sz = right - left;
5191 line->GetObjectSizes().Add(sz);
5192 }
5193 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5194 // Can break out of inner loop now since we've passed this line's range
5195 break;
5196
5197 node2 = node2->GetNext();
5198 }
5199
5200 lineNode = lineNode->GetNext();
5201 }
5202#endif
5203#endif
5204
5d7836c4
JS
5205 return true;
5206}
5207
603f702b
JS
5208/// Apply paragraph styles, such as centering, to wrapped lines
5209/// TODO: take into account box attributes, possibly
5210void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5211{
5212 if (!attr.HasAlignment())
5213 return;
5214
5215 wxPoint pos = line->GetPosition();
32423dd8 5216 wxPoint originalPos = pos;
603f702b
JS
5217 wxSize size = line->GetSize();
5218
5219 // centering, right-justification
8db2e3ef 5220 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5221 {
5222 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5223 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5224 line->SetPosition(pos);
5225 }
8db2e3ef 5226 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5227 {
5228 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5229 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5230 line->SetPosition(pos);
5231 }
32423dd8
JS
5232
5233 if (pos != originalPos)
5234 {
5235 wxPoint inc = pos - originalPos;
5236
5237 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5238
5239 while (node)
5240 {
5241 wxRichTextObject* child = node->GetData();
5242 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5243 child->Move(child->GetPosition() + inc);
5244
5245 node = node->GetNext();
5246 }
5247 }
603f702b 5248}
5d7836c4
JS
5249
5250/// Insert text at the given position
5251bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5252{
5253 wxRichTextObject* childToUse = NULL;
09f14108 5254 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5255
5256 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5257 while (node)
5258 {
5259 wxRichTextObject* child = node->GetData();
5260 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5261 {
5262 childToUse = child;
5263 nodeToUse = node;
5264 break;
5265 }
5266
5267 node = node->GetNext();
5268 }
5269
5270 if (childToUse)
5271 {
5272 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5273 if (textObject)
5274 {
5275 int posInString = pos - textObject->GetRange().GetStart();
5276
5277 wxString newText = textObject->GetText().Mid(0, posInString) +
5278 text + textObject->GetText().Mid(posInString);
5279 textObject->SetText(newText);
5280
28f92d74 5281 int textLength = text.length();
5d7836c4
JS
5282
5283 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5284 textObject->GetRange().GetEnd() + textLength));
5285
5286 // Increment the end range of subsequent fragments in this paragraph.
5287 // We'll set the paragraph range itself at a higher level.
5288
5289 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5290 while (node)
5291 {
5292 wxRichTextObject* child = node->GetData();
5293 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5294 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5295
5d7836c4
JS
5296 node = node->GetNext();
5297 }
5298
5299 return true;
5300 }
5301 else
5302 {
5303 // TODO: if not a text object, insert at closest position, e.g. in front of it
5304 }
5305 }
5306 else
5307 {
5308 // Add at end.
5309 // Don't pass parent initially to suppress auto-setting of parent range.
5310 // We'll do that at a higher level.
5311 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5312
5313 AppendChild(textObject);
5314 return true;
5315 }
5316
5317 return false;
5318}
5319
5320void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5321{
bec80f4f 5322 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5323}
5324
5325/// Clear the cached lines
5326void wxRichTextParagraph::ClearLines()
5327{
5328 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5329}
5330
5331/// Get/set the object size for the given range. Returns false if the range
5332/// is invalid for this object.
914a4e23 5333bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
5d7836c4
JS
5334{
5335 if (!range.IsWithin(GetRange()))
5336 return false;
5337
5338 if (flags & wxRICHTEXT_UNFORMATTED)
5339 {
5340 // Just use unformatted data, assume no line breaks
5d7836c4
JS
5341 wxSize sz;
5342
31778480
JS
5343 wxArrayInt childExtents;
5344 wxArrayInt* p;
5345 if (partialExtents)
5346 p = & childExtents;
5347 else
5348 p = NULL;
5349
a70eb13e
JS
5350 int maxDescent = 0;
5351 int maxAscent = 0;
5352 int maxLineHeight = 0;
5353
5d7836c4
JS
5354 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5355 while (node)
5356 {
5357 wxRichTextObject* child = node->GetData();
5358 if (!child->GetRange().IsOutside(range))
5359 {
cdaed652 5360 // Floating objects have a zero size within the paragraph.
e12b91a3 5361 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
cdaed652
VZ
5362 {
5363 if (partialExtents)
5364 {
5365 int lastSize;
5366 if (partialExtents->GetCount() > 0)
5367 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5368 else
5369 lastSize = 0;
5370
5371 partialExtents->Add(0 /* zero size */ + lastSize);
5372 }
5373 }
5374 else
5375 {
603f702b 5376 wxSize childSize;
4f3d5bc0 5377
603f702b
JS
5378 wxRichTextRange rangeToUse = range;
5379 rangeToUse.LimitTo(child->GetRange());
603f702b 5380 int childDescent = 0;
31778480 5381
7c9fdebe 5382 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
603f702b
JS
5383 // but it's only going to be used after caching has taken place.
5384 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5385 {
603f702b
JS
5386 childDescent = child->GetDescent();
5387 childSize = child->GetCachedSize();
2f45f554 5388
a70eb13e
JS
5389 if (childDescent == 0)
5390 {
5391 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5392 }
5393 else
5394 {
5395 maxDescent = wxMax(maxDescent, childDescent);
5396 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5397 }
5398
5399 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5400
5401 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5402 sz.x += childSize.x;
a70eb13e 5403 descent = maxDescent;
603f702b
JS
5404 }
5405 else if (child->IsTopLevel())
31778480 5406 {
603f702b
JS
5407 childDescent = child->GetDescent();
5408 childSize = child->GetCachedSize();
31778480 5409
a70eb13e
JS
5410 if (childDescent == 0)
5411 {
5412 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5413 }
5414 else
5415 {
5416 maxDescent = wxMax(maxDescent, childDescent);
5417 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5418 }
5419
5420 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5421
5422 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5423 sz.x += childSize.x;
a70eb13e
JS
5424 descent = maxDescent;
5425
5426 // FIXME: this won't change the original values.
5427 // Should we be calling GetRangeSize above instead of using cached values?
5428#if 0
603f702b 5429 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5430 {
603f702b
JS
5431 child->SetCachedSize(childSize);
5432 child->SetDescent(childDescent);
31778480 5433 }
a70eb13e 5434#endif
31778480 5435
603f702b
JS
5436 if (partialExtents)
5437 {
5438 int lastSize;
5439 if (partialExtents->GetCount() > 0)
5440 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5441 else
5442 lastSize = 0;
5443
5444 partialExtents->Add(childSize.x + lastSize);
5445 }
5446 }
914a4e23 5447 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
603f702b 5448 {
a70eb13e
JS
5449 if (childDescent == 0)
5450 {
5451 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5452 }
5453 else
5454 {
5455 maxDescent = wxMax(maxDescent, childDescent);
5456 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5457 }
5458
5459 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5460
5461 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5462 sz.x += childSize.x;
a70eb13e 5463 descent = maxDescent;
603f702b
JS
5464
5465 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5466 {
5467 child->SetCachedSize(childSize);
5468 child->SetDescent(childDescent);
5469 }
5470
5471 if (partialExtents)
5472 {
5473 int lastSize;
5474 if (partialExtents->GetCount() > 0)
5475 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5476 else
5477 lastSize = 0;
5478
5479 size_t i;
5480 for (i = 0; i < childExtents.GetCount(); i++)
5481 {
5482 partialExtents->Add(childExtents[i] + lastSize);
5483 }
5484 }
5485 }
5486 }
5487
5488 if (p)
5489 p->Clear();
5d7836c4
JS
5490 }
5491
5492 node = node->GetNext();
5493 }
5494 size = sz;
5495 }
5496 else
5497 {
5498 // Use formatted data, with line breaks
5499 wxSize sz;
5500
5501 // We're going to loop through each line, and then for each line,
5502 // call GetRangeSize for the fragment that comprises that line.
5503 // Only we have to do that multiple times within the line, because
5504 // the line may be broken into pieces. For now ignore line break commands
5505 // (so we can assume that getting the unformatted size for a fragment
5506 // within a line is the actual size)
5507
5508 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5509 while (node)
5510 {
5511 wxRichTextLine* line = node->GetData();
1e967276
JS
5512 wxRichTextRange lineRange = line->GetAbsoluteRange();
5513 if (!lineRange.IsOutside(range))
5d7836c4 5514 {
a70eb13e
JS
5515 int maxDescent = 0;
5516 int maxAscent = 0;
5517 int maxLineHeight = 0;
5518 int maxLineWidth = 0;
7fe8059f 5519
5d7836c4
JS
5520 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5521 while (node2)
5522 {
5523 wxRichTextObject* child = node2->GetData();
7fe8059f 5524
e12b91a3 5525 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5d7836c4 5526 {
1e967276 5527 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5528 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5529 if (child->IsTopLevel())
5530 rangeToUse = child->GetOwnRange();
7fe8059f 5531
5d7836c4
JS
5532 wxSize childSize;
5533 int childDescent = 0;
914a4e23 5534 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
5d7836c4 5535 {
a70eb13e
JS
5536 if (childDescent == 0)
5537 {
5538 // Assume that if descent is zero, this child can occupy the full line height
5539 // and does not need space for the line's maximum descent. So we influence
5540 // the overall max line height only.
5541 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5542 }
5543 else
5544 {
5545 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5546 maxDescent = wxMax(maxAscent, childDescent);
5547 }
5548 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5549 maxLineWidth += childSize.x;
5d7836c4 5550 }
5d7836c4 5551 }
7fe8059f 5552
5d7836c4
JS
5553 node2 = node2->GetNext();
5554 }
5555
a70eb13e
JS
5556 descent = wxMax(descent, maxDescent);
5557
5d7836c4 5558 // Increase size by a line (TODO: paragraph spacing)
a70eb13e
JS
5559 sz.y += maxLineHeight;
5560 sz.x = wxMax(sz.x, maxLineWidth);
5d7836c4
JS
5561 }
5562 node = node->GetNext();
5563 }
5564 size = sz;
5565 }
5566 return true;
5567}
5568
5569/// Finds the absolute position and row height for the given character position
8db2e3ef 5570bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5571{
5572 if (index == -1)
5573 {
5574 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5575 if (line)
5576 *height = line->GetSize().y;
5577 else
5578 *height = dc.GetCharHeight();
5579
5580 // -1 means 'the start of the buffer'.
5581 pt = GetPosition();
5582 if (line)
5583 pt = pt + line->GetPosition();
5584
5d7836c4
JS
5585 return true;
5586 }
5587
5588 // The final position in a paragraph is taken to mean the position
5589 // at the start of the next paragraph.
5590 if (index == GetRange().GetEnd())
5591 {
5592 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5593 wxASSERT( parent != NULL );
5594
5595 // Find the height at the next paragraph, if any
5596 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5597 if (line)
5598 {
5599 *height = line->GetSize().y;
5600 pt = line->GetAbsolutePosition();
5601 }
5602 else
5603 {
5604 *height = dc.GetCharHeight();
5605 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5606 pt = wxPoint(indent, GetCachedSize().y);
5607 }
5608
5609 return true;
5610 }
5611
5612 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5613 return false;
5614
5615 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5616 while (node)
5617 {
5618 wxRichTextLine* line = node->GetData();
1e967276
JS
5619 wxRichTextRange lineRange = line->GetAbsoluteRange();
5620 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5621 {
5622 // If this is the last point in the line, and we're forcing the
5623 // returned value to be the start of the next line, do the required
5624 // thing.
1e967276 5625 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5626 {
5627 if (node->GetNext())
5628 {
5629 wxRichTextLine* nextLine = node->GetNext()->GetData();
5630 *height = nextLine->GetSize().y;
5631 pt = nextLine->GetAbsolutePosition();
5632 return true;
5633 }
5634 }
5635
5636 pt.y = line->GetPosition().y + GetPosition().y;
5637
1e967276 5638 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5639 wxSize rangeSize;
5640 int descent = 0;
5641
5642 // We find the size of the line up to this point,
5643 // then we can add this size to the line start position and
5644 // paragraph start position to find the actual position.
5645
8db2e3ef 5646 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5647 {
5648 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5649 *height = line->GetSize().y;
5650
5651 return true;
5652 }
5653
5654 }
5655
5656 node = node->GetNext();
5657 }
5658
5659 return false;
5660}
5661
5662/// Hit-testing: returns a flag indicating hit test details, plus
5663/// information about position
8db2e3ef 5664int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5665{
603f702b
JS
5666 if (!IsShown())
5667 return wxRICHTEXT_HITTEST_NONE;
5668
5669 // If we're in the top-level container, then we can return
5670 // a suitable hit test code even if the point is outside the container area,
5671 // so that we can position the caret sensibly even if we don't
5672 // click on valid content. If we're not at the top-level, and the point
5673 // is not within this paragraph object, then we don't want to stop more
5674 // precise hit-testing from working prematurely, so return immediately.
5675 // NEW STRATEGY: use the parent boundary to test whether we're in the
5676 // right region, not the paragraph, since the paragraph may be positioned
5677 // some way in from where the user clicks.
5678 {
5679 long tmpPos;
5680 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5681 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5682 return wxRICHTEXT_HITTEST_NONE;
5683 }
5684
5685 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5686 while (objNode)
5687 {
5688 wxRichTextObject* child = objNode->GetData();
7c9fdebe
JS
5689 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5690 // and also, if this seems composite but actually is marked as atomic,
5691 // don't recurse.
5692 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5693 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
603f702b
JS
5694 {
5695 {
8db2e3ef 5696 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5697 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5698 return hitTest;
5699 }
5700 }
5701
5702 objNode = objNode->GetNext();
5703 }
5704
5d7836c4
JS
5705 wxPoint paraPos = GetPosition();
5706
5707 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5708 while (node)
5709 {
5710 wxRichTextLine* line = node->GetData();
5711 wxPoint linePos = paraPos + line->GetPosition();
5712 wxSize lineSize = line->GetSize();
1e967276 5713 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5714
62381daa 5715 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5716 {
5717 if (pt.x < linePos.x)
5718 {
1e967276 5719 textPosition = lineRange.GetStart();
603f702b
JS
5720 *obj = FindObjectAtPosition(textPosition);
5721 *contextObj = GetContainer();
f262b25c 5722 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5723 }
5724 else if (pt.x >= (linePos.x + lineSize.x))
5725 {
1e967276 5726 textPosition = lineRange.GetEnd();
603f702b
JS
5727 *obj = FindObjectAtPosition(textPosition);
5728 *contextObj = GetContainer();
f262b25c 5729 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5730 }
5731 else
5732 {
2f45f554
JS
5733#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5734 wxArrayInt partialExtents;
5735
5736 wxSize paraSize;
5737 int paraDescent;
5738
5739 // This calculates the partial text extents
914a4e23 5740 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
2f45f554
JS
5741
5742 int lastX = linePos.x;
5743 size_t i;
5744 for (i = 0; i < partialExtents.GetCount(); i++)
5745 {
5746 int nextX = partialExtents[i] + linePos.x;
5747
5748 if (pt.x >= lastX && pt.x <= nextX)
5749 {
5750 textPosition = i + lineRange.GetStart(); // minus 1?
5751
603f702b
JS
5752 *obj = FindObjectAtPosition(textPosition);
5753 *contextObj = GetContainer();
5754
2f45f554
JS
5755 // So now we know it's between i-1 and i.
5756 // Let's see if we can be more precise about
5757 // which side of the position it's on.
5758
cdaed652 5759 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5760 if (pt.x >= midPoint)
5761 return wxRICHTEXT_HITTEST_AFTER;
5762 else
5763 return wxRICHTEXT_HITTEST_BEFORE;
5764 }
5765
5766 lastX = nextX;
5767 }
5768#else
5d7836c4
JS
5769 long i;
5770 int lastX = linePos.x;
1e967276 5771 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5772 {
5773 wxSize childSize;
5774 int descent = 0;
7fe8059f 5775
1e967276 5776 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5777
8db2e3ef 5778 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5779
5780 int nextX = childSize.x + linePos.x;
5781
5782 if (pt.x >= lastX && pt.x <= nextX)
5783 {
5784 textPosition = i;
5785
603f702b
JS
5786 *obj = FindObjectAtPosition(textPosition);
5787 *contextObj = GetContainer();
5788
5d7836c4
JS
5789 // So now we know it's between i-1 and i.
5790 // Let's see if we can be more precise about
5791 // which side of the position it's on.
5792
cdaed652 5793 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5794 if (pt.x >= midPoint)
5795 return wxRICHTEXT_HITTEST_AFTER;
5796 else
5797 return wxRICHTEXT_HITTEST_BEFORE;
5798 }
5799 else
5800 {
5801 lastX = nextX;
5802 }
5803 }
2f45f554 5804#endif
5d7836c4
JS
5805 }
5806 }
7fe8059f 5807
5d7836c4
JS
5808 node = node->GetNext();
5809 }
5810
5811 return wxRICHTEXT_HITTEST_NONE;
5812}
5813
5814/// Split an object at this position if necessary, and return
5815/// the previous object, or NULL if inserting at beginning.
5816wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5817{
5818 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5819 while (node)
5820 {
5821 wxRichTextObject* child = node->GetData();
5822
5823 if (pos == child->GetRange().GetStart())
5824 {
5825 if (previousObject)
4d551ad5
JS
5826 {
5827 if (node->GetPrevious())
5828 *previousObject = node->GetPrevious()->GetData();
5829 else
5830 *previousObject = NULL;
5831 }
5d7836c4
JS
5832
5833 return child;
5834 }
5835
5836 if (child->GetRange().Contains(pos))
5837 {
5838 // This should create a new object, transferring part of
5839 // the content to the old object and the rest to the new object.
5840 wxRichTextObject* newObject = child->DoSplit(pos);
5841
5842 // If we couldn't split this object, just insert in front of it.
5843 if (!newObject)
5844 {
5845 // Maybe this is an empty string, try the next one
5846 // return child;
5847 }
5848 else
5849 {
5850 // Insert the new object after 'child'
5851 if (node->GetNext())
5852 m_children.Insert(node->GetNext(), newObject);
5853 else
5854 m_children.Append(newObject);
5855 newObject->SetParent(this);
5856
5857 if (previousObject)
5858 *previousObject = child;
5859
5860 return newObject;
5861 }
5862 }
5863
5864 node = node->GetNext();
5865 }
5866 if (previousObject)
5867 *previousObject = NULL;
5868 return NULL;
5869}
5870
5871/// Move content to a list from obj on
5872void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5873{
5874 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5875 while (node)
5876 {
5877 wxRichTextObject* child = node->GetData();
5878 list.Append(child);
5879
5880 wxRichTextObjectList::compatibility_iterator oldNode = node;
5881
5882 node = node->GetNext();
5883
5884 m_children.DeleteNode(oldNode);
5885 }
5886}
5887
5888/// Add content back from list
5889void wxRichTextParagraph::MoveFromList(wxList& list)
5890{
09f14108 5891 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5892 {
5893 AppendChild((wxRichTextObject*) node->GetData());
5894 }
5895}
5896
5897/// Calculate range
5898void wxRichTextParagraph::CalculateRange(long start, long& end)
5899{
5900 wxRichTextCompositeObject::CalculateRange(start, end);
5901
5902 // Add one for end of paragraph
5903 end ++;
5904
5905 m_range.SetRange(start, end);
5906}
5907
5908/// Find the object at the given position
5909wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5910{
5911 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5912 while (node)
5913 {
5914 wxRichTextObject* obj = node->GetData();
603f702b
JS
5915 if (obj->GetRange().Contains(position) ||
5916 obj->GetRange().GetStart() == position ||
5917 obj->GetRange().GetEnd() == position)
5d7836c4 5918 return obj;
7fe8059f 5919
5d7836c4
JS
5920 node = node->GetNext();
5921 }
5922 return NULL;
5923}
5924
5925/// Get the plain text searching from the start or end of the range.
5926/// The resulting string may be shorter than the range given.
5927bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5928{
5929 text = wxEmptyString;
5930
5931 if (fromStart)
5932 {
5933 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5934 while (node)
5935 {
5936 wxRichTextObject* obj = node->GetData();
5937 if (!obj->GetRange().IsOutside(range))
5938 {
5939 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5940 if (textObj)
5941 {
5942 text += textObj->GetTextForRange(range);
5943 }
5944 else
043c0d58
JS
5945 {
5946 text += wxT(" ");
5947 }
5d7836c4
JS
5948 }
5949
5950 node = node->GetNext();
5951 }
5952 }
5953 else
5954 {
5955 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5956 while (node)
5957 {
5958 wxRichTextObject* obj = node->GetData();
5959 if (!obj->GetRange().IsOutside(range))
5960 {
5961 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5962 if (textObj)
5963 {
5964 text = textObj->GetTextForRange(range) + text;
5965 }
5966 else
043c0d58
JS
5967 {
5968 text = wxT(" ") + text;
5969 }
5d7836c4
JS
5970 }
5971
5972 node = node->GetPrevious();
5973 }
5974 }
5975
5976 return true;
5977}
5978
5979/// Find a suitable wrap position.
8db2e3ef 5980bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5981{
72945e24
JS
5982 if (range.GetLength() <= 0)
5983 return false;
5984
5d7836c4
JS
5985 // Find the first position where the line exceeds the available space.
5986 wxSize sz;
5d7836c4 5987 long breakPosition = range.GetEnd();
ecb5fbf1 5988
31778480
JS
5989#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5990 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 5991 {
31778480 5992 int widthBefore;
5d7836c4 5993
31778480
JS
5994 if (range.GetStart() > GetRange().GetStart())
5995 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5996 else
5997 widthBefore = 0;
5998
5999 size_t i;
43a0d1e1 6000 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 6001 {
31778480 6002 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 6003
72945e24 6004 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 6005 {
31778480
JS
6006 breakPosition = i-1;
6007 break;
ecb5fbf1 6008 }
5d7836c4 6009 }
31778480
JS
6010 }
6011 else
6012#endif
6013 {
6014 // Binary chop for speed
6015 long minPos = range.GetStart();
6016 long maxPos = range.GetEnd();
6017 while (true)
ecb5fbf1 6018 {
31778480
JS
6019 if (minPos == maxPos)
6020 {
6021 int descent = 0;
8db2e3ef 6022 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 6023
31778480
JS
6024 if (sz.x > availableSpace)
6025 breakPosition = minPos - 1;
6026 break;
6027 }
6028 else if ((maxPos - minPos) == 1)
ecb5fbf1 6029 {
31778480 6030 int descent = 0;
8db2e3ef 6031 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6032
6033 if (sz.x > availableSpace)
6034 breakPosition = minPos - 1;
6035 else
6036 {
8db2e3ef 6037 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6038 if (sz.x > availableSpace)
6039 breakPosition = maxPos-1;
6040 }
6041 break;
ecb5fbf1
JS
6042 }
6043 else
6044 {
31778480
JS
6045 long nextPos = minPos + ((maxPos - minPos) / 2);
6046
6047 int descent = 0;
8db2e3ef 6048 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
6049
6050 if (sz.x > availableSpace)
6051 {
6052 maxPos = nextPos;
6053 }
6054 else
6055 {
6056 minPos = nextPos;
6057 }
ecb5fbf1
JS
6058 }
6059 }
5d7836c4
JS
6060 }
6061
6062 // Now we know the last position on the line.
6063 // Let's try to find a word break.
6064
6065 wxString plainText;
6066 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6067 {
ff76711f
JS
6068 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6069 if (newLinePos != wxNOT_FOUND)
5d7836c4 6070 {
ff76711f
JS
6071 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6072 }
6073 else
6074 {
6075 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
6076 int tabPos = plainText.Find(wxT('\t'), true);
6077 int pos = wxMax(spacePos, tabPos);
6078 if (pos != wxNOT_FOUND)
ff76711f 6079 {
31002e44 6080 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
6081 breakPosition = breakPosition - positionsFromEndOfString;
6082 }
5d7836c4
JS
6083 }
6084 }
6085
6086 wrapPosition = breakPosition;
6087
6088 return true;
6089}
6090
6091/// Get the bullet text for this paragraph.
6092wxString wxRichTextParagraph::GetBulletText()
6093{
6094 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6095 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6096 return wxEmptyString;
6097
6098 int number = GetAttributes().GetBulletNumber();
6099
6100 wxString text;
d2d0adc7 6101 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
6102 {
6103 text.Printf(wxT("%d"), number);
6104 }
6105 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6106 {
6107 // TODO: Unicode, and also check if number > 26
6108 text.Printf(wxT("%c"), (wxChar) (number+64));
6109 }
6110 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6111 {
6112 // TODO: Unicode, and also check if number > 26
6113 text.Printf(wxT("%c"), (wxChar) (number+96));
6114 }
6115 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6116 {
59509217 6117 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
6118 }
6119 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6120 {
59509217
JS
6121 text = wxRichTextDecimalToRoman(number);
6122 text.MakeLower();
5d7836c4
JS
6123 }
6124 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6125 {
d2d0adc7
JS
6126 text = GetAttributes().GetBulletText();
6127 }
3e541562 6128
d2d0adc7
JS
6129 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6130 {
6131 // The outline style relies on the text being computed statically,
6132 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6133 // should be stored in the attributes; if not, just use the number for this
6134 // level, as previously computed.
6135 if (!GetAttributes().GetBulletText().IsEmpty())
6136 text = GetAttributes().GetBulletText();
5d7836c4
JS
6137 }
6138
6139 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6140 {
6141 text = wxT("(") + text + wxT(")");
6142 }
d2d0adc7
JS
6143 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6144 {
6145 text = text + wxT(")");
6146 }
6147
5d7836c4
JS
6148 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6149 {
6150 text += wxT(".");
6151 }
6152
6153 return text;
6154}
6155
1e967276
JS
6156/// Allocate or reuse a line object
6157wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6158{
6159 if (pos < (int) m_cachedLines.GetCount())
6160 {
6161 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6162 line->Init(this);
6163 return line;
6164 }
6165 else
6166 {
6167 wxRichTextLine* line = new wxRichTextLine(this);
6168 m_cachedLines.Append(line);
6169 return line;
6170 }
6171}
6172
6173/// Clear remaining unused line objects, if any
6174bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6175{
6176 int cachedLineCount = m_cachedLines.GetCount();
6177 if ((int) cachedLineCount > lineCount)
6178 {
6179 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6180 {
6181 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6182 wxRichTextLine* line = node->GetData();
6183 m_cachedLines.Erase(node);
6184 delete line;
6185 }
6186 }
6187 return true;
6188}
6189
fe5aa22c
JS
6190/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6191/// retrieve the actual style.
603f702b 6192wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 6193{
24777478 6194 wxRichTextAttr attr;
603f702b 6195 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6196 if (buf)
6197 {
6198 attr = buf->GetBasicStyle();
603f702b
JS
6199 if (!includingBoxAttr)
6200 {
6201 attr.GetTextBoxAttr().Reset();
6202 // The background colour will be painted by the container, and we don't
6203 // want to unnecessarily overwrite the background when we're drawing text
6204 // because this may erase the guideline (which appears just under the text
6205 // if there's no padding).
6206 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6207 }
fe5aa22c
JS
6208 wxRichTextApplyStyle(attr, GetAttributes());
6209 }
6210 else
6211 attr = GetAttributes();
6212
6213 wxRichTextApplyStyle(attr, contentStyle);
6214 return attr;
6215}
6216
6217/// Get combined attributes of the base style and paragraph style.
603f702b 6218wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 6219{
24777478 6220 wxRichTextAttr attr;
603f702b 6221 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6222 if (buf)
6223 {
6224 attr = buf->GetBasicStyle();
603f702b
JS
6225 if (!includingBoxAttr)
6226 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
6227 wxRichTextApplyStyle(attr, GetAttributes());
6228 }
6229 else
6230 attr = GetAttributes();
6231
6232 return attr;
6233}
5d7836c4 6234
603f702b 6235// Create default tabstop array
cfa3b256
JS
6236void wxRichTextParagraph::InitDefaultTabs()
6237{
6238 // create a default tab list at 10 mm each.
6239 for (int i = 0; i < 20; ++i)
6240 {
6241 sm_defaultTabs.Add(i*100);
6242 }
6243}
6244
603f702b 6245// Clear default tabstop array
cfa3b256
JS
6246void wxRichTextParagraph::ClearDefaultTabs()
6247{
6248 sm_defaultTabs.Clear();
6249}
6250
c4168888 6251void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
6252{
6253 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6254 while (node)
6255 {
bec80f4f 6256 wxRichTextObject* anchored = node->GetData();
07d4142f 6257 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652 6258 {
c4168888
JS
6259 int x = 0;
6260 wxRichTextAttr parentAttr(GetAttributes());
6261 context.ApplyVirtualAttributes(parentAttr, this);
6262#if 1
6263 // 27-09-2012
6264 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6265
6266 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6267 parentAttr, anchored->GetAttributes(),
6268 parentRect, availableSpace,
6269 style);
6270 wxSize size = anchored->GetCachedSize();
6271#else
cdaed652 6272 wxSize size;
c4168888 6273 int descent = 0;
8db2e3ef 6274 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
c4168888 6275#endif
bec80f4f 6276
24777478 6277 int offsetY = 0;
603f702b 6278 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
6279 {
6280 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6281 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6282 {
6283 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6284 }
6285 }
bec80f4f 6286
24777478 6287 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6288
cdaed652 6289 /* Update the offset */
24777478
JS
6290 int newOffsetY = pos - rect.y;
6291 if (newOffsetY != offsetY)
6292 {
6293 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6294 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6295 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6296 }
cdaed652 6297
24777478 6298 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6299 x = rect.x;
24777478 6300 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6301 x = rect.x + rect.width - size.x;
24777478 6302
c4168888
JS
6303 //anchored->SetPosition(wxPoint(x, pos));
6304 anchored->Move(wxPoint(x, pos)); // should move children
cdaed652
VZ
6305 anchored->SetCachedSize(size);
6306 floatCollector->CollectFloat(this, anchored);
6307 }
6308
6309 node = node->GetNext();
6310 }
6311}
6312
603f702b 6313// Get the first position from pos that has a line break character.
ff76711f
JS
6314long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6315{
6316 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6317 while (node)
6318 {
6319 wxRichTextObject* obj = node->GetData();
6320 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6321 {
6322 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6323 if (textObj)
6324 {
6325 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6326 if (breakPos > -1)
6327 return breakPos;
6328 }
6329 }
6330 node = node->GetNext();
6331 }
6332 return -1;
6333}
cfa3b256 6334
5d7836c4
JS
6335/*!
6336 * wxRichTextLine
6337 * This object represents a line in a paragraph, and stores
6338 * offsets from the start of the paragraph representing the
6339 * start and end positions of the line.
6340 */
6341
6342wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6343{
1e967276 6344 Init(parent);
5d7836c4
JS
6345}
6346
6347/// Initialisation
1e967276 6348void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6349{
1e967276
JS
6350 m_parent = parent;
6351 m_range.SetRange(-1, -1);
6352 m_pos = wxPoint(0, 0);
6353 m_size = wxSize(0, 0);
5d7836c4 6354 m_descent = 0;
2f45f554
JS
6355#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6356 m_objectSizes.Clear();
6357#endif
5d7836c4
JS
6358}
6359
6360/// Copy
6361void wxRichTextLine::Copy(const wxRichTextLine& obj)
6362{
6363 m_range = obj.m_range;
2f45f554
JS
6364#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6365 m_objectSizes = obj.m_objectSizes;
6366#endif
5d7836c4
JS
6367}
6368
6369/// Get the absolute object position
6370wxPoint wxRichTextLine::GetAbsolutePosition() const
6371{
6372 return m_parent->GetPosition() + m_pos;
6373}
6374
1e967276
JS
6375/// Get the absolute range
6376wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6377{
6378 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6379 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6380 return range;
6381}
6382
5d7836c4
JS
6383/*!
6384 * wxRichTextPlainText
6385 * This object represents a single piece of text.
6386 */
6387
6388IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6389
24777478 6390wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6391 wxRichTextObject(parent)
6392{
5d7836c4
JS
6393 if (style)
6394 SetAttributes(*style);
6395
6396 m_text = text;
6397}
6398
cfa3b256
JS
6399#define USE_KERNING_FIX 1
6400
4794d69c
JS
6401// If insufficient tabs are defined, this is the tab width used
6402#define WIDTH_FOR_DEFAULT_TABS 50
6403
5d7836c4 6404/// Draw the item
8db2e3ef 6405bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6406{
fe5aa22c
JS
6407 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6408 wxASSERT (para != NULL);
6409
603f702b 6410 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6411 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6412
6413 // Let's make the assumption for now that for content in a paragraph, including
6414 // text, we never have a discontinuous selection. So we only deal with a
6415 // single range.
6416 wxRichTextRange selectionRange;
6417 if (selection.IsValid())
6418 {
6419 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6420 if (selectionRanges.GetCount() > 0)
6421 selectionRange = selectionRanges[0];
6422 else
6423 selectionRange = wxRICHTEXT_NO_SELECTION;
6424 }
6425 else
6426 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6427
5d7836c4
JS
6428 int offset = GetRange().GetStart();
6429
ff76711f 6430 wxString str = m_text;
f7667b84
JS
6431 if (context.HasVirtualText(this))
6432 {
6433 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6434 str = m_text;
6435 }
6436
6437 // Replace line break characters with spaces
ff76711f
JS
6438 wxString toRemove = wxRichTextLineBreakChar;
6439 str.Replace(toRemove, wxT(" "));
d07f2e19 6440 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
c025e094 6441 str.MakeUpper();
3e541562 6442
5d7836c4 6443 long len = range.GetLength();
ff76711f 6444 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6445
5d7836c4
JS
6446 // Test for the optimized situations where all is selected, or none
6447 // is selected.
6448
30bf7630
JS
6449 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6450 wxCheckSetFont(dc, textFont);
6451 int charHeight = dc.GetCharHeight();
6452
6453 int x, y;
a1b806b9 6454 if ( textFont.IsOk() )
30bf7630 6455 {
d07f2e19
JS
6456 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6457 {
6458 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6459 wxCheckSetFont(dc, textFont);
6460 charHeight = dc.GetCharHeight();
6461 }
6462
30bf7630
JS
6463 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6464 {
32423dd8
JS
6465 if (textFont.IsUsingSizeInPixels())
6466 {
6467 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
4ba36292 6468 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
32423dd8
JS
6469 x = rect.x;
6470 y = rect.y;
6471 }
6472 else
6473 {
6474 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6475 textFont.SetPointSize(static_cast<int>(size));
32423dd8
JS
6476 x = rect.x;
6477 y = rect.y;
6478 }
30bf7630
JS
6479 wxCheckSetFont(dc, textFont);
6480 }
6481 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6482 {
32423dd8
JS
6483 if (textFont.IsUsingSizeInPixels())
6484 {
6485 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6486 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6487 x = rect.x;
4ba36292 6488 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6489 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6490 }
6491 else
6492 {
6493 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6494 textFont.SetPointSize(static_cast<int>(size));
32423dd8 6495 x = rect.x;
4ba36292 6496 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6497 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6498 }
30bf7630
JS
6499 wxCheckSetFont(dc, textFont);
6500 }
6501 else
6502 {
6503 x = rect.x;
6504 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6505 }
6506 }
6507 else
6508 {
6509 x = rect.x;
6510 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6511 }
5d7836c4 6512
603f702b
JS
6513 // TODO: new selection code
6514
5d7836c4
JS
6515 // (a) All selected.
6516 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6517 {
fe5aa22c 6518 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6519 }
6520 // (b) None selected.
6521 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6522 {
6523 // Draw all unselected
fe5aa22c 6524 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6525 }
6526 else
6527 {
6528 // (c) Part selected, part not
6529 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6530
04ee05f9 6531 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6532
5d7836c4
JS
6533 // 1. Initial unselected chunk, if any, up until start of selection.
6534 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6535 {
6536 int r1 = range.GetStart();
6537 int s1 = selectionRange.GetStart()-1;
6538 int fragmentLen = s1 - r1 + 1;
6539 if (fragmentLen < 0)
af588446 6540 {
5d7836c4 6541 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6542 }
ff76711f 6543 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6544
fe5aa22c 6545 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6546
6547#if USE_KERNING_FIX
6548 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6549 {
6550 // Compensate for kerning difference
ff76711f
JS
6551 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6552 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6553
cfa3b256
JS
6554 wxCoord w1, h1, w2, h2, w3, h3;
6555 dc.GetTextExtent(stringFragment, & w1, & h1);
6556 dc.GetTextExtent(stringFragment2, & w2, & h2);
6557 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6558
cfa3b256
JS
6559 int kerningDiff = (w1 + w3) - w2;
6560 x = x - kerningDiff;
6561 }
6562#endif
5d7836c4
JS
6563 }
6564
6565 // 2. Selected chunk, if any.
6566 if (selectionRange.GetEnd() >= range.GetStart())
6567 {
6568 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6569 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6570
6571 int fragmentLen = s2 - s1 + 1;
6572 if (fragmentLen < 0)
af588446 6573 {
5d7836c4 6574 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6575 }
ff76711f 6576 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6577
fe5aa22c 6578 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6579
6580#if USE_KERNING_FIX
6581 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6582 {
6583 // Compensate for kerning difference
ff76711f
JS
6584 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6585 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6586
cfa3b256
JS
6587 wxCoord w1, h1, w2, h2, w3, h3;
6588 dc.GetTextExtent(stringFragment, & w1, & h1);
6589 dc.GetTextExtent(stringFragment2, & w2, & h2);
6590 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6591
cfa3b256
JS
6592 int kerningDiff = (w1 + w3) - w2;
6593 x = x - kerningDiff;
6594 }
6595#endif
5d7836c4
JS
6596 }
6597
6598 // 3. Remaining unselected chunk, if any
6599 if (selectionRange.GetEnd() < range.GetEnd())
6600 {
6601 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6602 int r2 = range.GetEnd();
6603
6604 int fragmentLen = r2 - s2 + 1;
6605 if (fragmentLen < 0)
af588446 6606 {
5d7836c4 6607 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6608 }
ff76711f 6609 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6610
fe5aa22c 6611 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6612 }
5d7836c4
JS
6613 }
6614
6615 return true;
6616}
61399247 6617
24777478 6618bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6619{
cfa3b256
JS
6620 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6621
6622 wxArrayInt tabArray;
6623 int tabCount;
6624 if (hasTabs)
ab14c7aa 6625 {
cfa3b256
JS
6626 if (attr.GetTabs().IsEmpty())
6627 tabArray = wxRichTextParagraph::GetDefaultTabs();
6628 else
6629 tabArray = attr.GetTabs();
6630 tabCount = tabArray.GetCount();
6631
6632 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6633 {
cfa3b256
JS
6634 int pos = tabArray[i];
6635 pos = ConvertTenthsMMToPixels(dc, pos);
6636 tabArray[i] = pos;
7f0d9d71
JS
6637 }
6638 }
cfa3b256
JS
6639 else
6640 tabCount = 0;
ab14c7aa 6641
cfa3b256
JS
6642 int nextTabPos = -1;
6643 int tabPos = -1;
7f0d9d71 6644 wxCoord w, h;
ab14c7aa 6645
cfa3b256 6646 if (selected)
ab14c7aa 6647 {
0ec6da02
JS
6648 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6649 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6650
ecb5fbf1
JS
6651 wxCheckSetBrush(dc, wxBrush(highlightColour));
6652 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6653 dc.SetTextForeground(highlightTextColour);
04ee05f9 6654 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6655 }
ab14c7aa
JS
6656 else
6657 {
fe5aa22c 6658 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6659
f0e9eda2
JS
6660 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6661 {
04ee05f9 6662 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6663 dc.SetTextBackground(attr.GetBackgroundColour());
6664 }
6665 else
04ee05f9 6666 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6667 }
3e541562 6668
925a662a 6669 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6670 while (hasTabs)
ab14c7aa
JS
6671 {
6672 // the string has a tab
7f0d9d71
JS
6673 // break up the string at the Tab
6674 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6675 str = str.AfterFirst(wxT('\t'));
6676 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6677 tabPos = x + w;
7f0d9d71 6678 bool not_found = true;
cfa3b256 6679 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6680 {
015d0446 6681 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6682
6683 // Find the next tab position.
6684 // Even if we're at the end of the tab array, we must still draw the chunk.
6685
6686 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6687 {
4794d69c
JS
6688 if (nextTabPos <= tabPos)
6689 {
6690 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6691 nextTabPos = tabPos + defaultTabWidth;
6692 }
6693
7f0d9d71 6694 not_found = false;
ab14c7aa
JS
6695 if (selected)
6696 {
cfa3b256 6697 w = nextTabPos - x;
7f0d9d71 6698 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6699 dc.DrawRectangle(selRect);
7f0d9d71
JS
6700 }
6701 dc.DrawText(stringChunk, x, y);
42688aea
JS
6702
6703 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6704 {
6705 wxPen oldPen = dc.GetPen();
ecb5fbf1 6706 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6707 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6708 wxCheckSetPen(dc, oldPen);
42688aea
JS
6709 }
6710
cfa3b256 6711 x = nextTabPos;
7f0d9d71
JS
6712 }
6713 }
cfa3b256 6714 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6715 }
61399247 6716
cfa3b256 6717 if (!str.IsEmpty())
ab14c7aa 6718 {
cfa3b256
JS
6719 dc.GetTextExtent(str, & w, & h);
6720 if (selected)
6721 {
6722 wxRect selRect(x, rect.y, w, rect.GetHeight());
6723 dc.DrawRectangle(selRect);
6724 }
6725 dc.DrawText(str, x, y);
42688aea
JS
6726
6727 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6728 {
6729 wxPen oldPen = dc.GetPen();
ecb5fbf1 6730 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6731 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6732 wxCheckSetPen(dc, oldPen);
42688aea
JS
6733 }
6734
cfa3b256 6735 x += w;
7f0d9d71 6736 }
5d7836c4 6737
7c9fdebe 6738 return true;
7f0d9d71 6739}
fe5aa22c 6740
5d7836c4 6741/// Lay the item out
8db2e3ef 6742bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6743{
ecb5fbf1
JS
6744 // Only lay out if we haven't already cached the size
6745 if (m_size.x == -1)
8db2e3ef 6746 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6747 m_maxSize = m_size;
6748 // Eventually we want to have a reasonable estimate of minimum size.
6749 m_minSize = wxSize(0, 0);
5d7836c4
JS
6750 return true;
6751}
6752
6753/// Copy
6754void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6755{
6756 wxRichTextObject::Copy(obj);
6757
6758 m_text = obj.m_text;
6759}
6760
6761/// Get/set the object size for the given range. Returns false if the range
6762/// is invalid for this object.
914a4e23 6763bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), const wxPoint& position, const wxSize& WXUNUSED(parentSize), wxArrayInt* partialExtents) const
5d7836c4
JS
6764{
6765 if (!range.IsWithin(GetRange()))
6766 return false;
6767
fe5aa22c
JS
6768 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6769 wxASSERT (para != NULL);
603f702b 6770
925a662a 6771 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6772
24777478 6773 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6774 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6775
5d7836c4
JS
6776 // Always assume unformatted text, since at this level we have no knowledge
6777 // of line breaks - and we don't need it, since we'll calculate size within
6778 // formatted text by doing it in chunks according to the line ranges
6779
30bf7630 6780 bool bScript(false);
44cc96a8 6781 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6782 if (font.IsOk())
30bf7630
JS
6783 {
6784 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6785 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6786 {
6787 wxFont textFont = font;
32423dd8
JS
6788 if (textFont.IsUsingSizeInPixels())
6789 {
6790 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6791 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6792 }
6793 else
6794 {
6795 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6796 textFont.SetPointSize(static_cast<int>(size));
6797 }
30bf7630
JS
6798 wxCheckSetFont(dc, textFont);
6799 bScript = true;
6800 }
d07f2e19
JS
6801 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6802 {
6803 wxFont textFont = font;
6804 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6805 wxCheckSetFont(dc, textFont);
6806 bScript = true;
6807 }
30bf7630
JS
6808 else
6809 {
6810 wxCheckSetFont(dc, font);
6811 }
6812 }
5d7836c4 6813
109bfc88 6814 bool haveDescent = false;
5d7836c4
JS
6815 int startPos = range.GetStart() - GetRange().GetStart();
6816 long len = range.GetLength();
3e541562 6817
ff76711f 6818 wxString str(m_text);
f7667b84
JS
6819 if (context.HasVirtualText(this))
6820 {
6821 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6822 str = m_text;
6823 }
6824
ff76711f
JS
6825 wxString toReplace = wxRichTextLineBreakChar;
6826 str.Replace(toReplace, wxT(" "));
6827
6828 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea 6829
d07f2e19 6830 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
42688aea
JS
6831 stringChunk.MakeUpper();
6832
5d7836c4 6833 wxCoord w, h;
7f0d9d71 6834 int width = 0;
cfa3b256 6835 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6836 {
6837 // the string has a tab
cfa3b256
JS
6838 wxArrayInt tabArray;
6839 if (textAttr.GetTabs().IsEmpty())
6840 tabArray = wxRichTextParagraph::GetDefaultTabs();
6841 else
6842 tabArray = textAttr.GetTabs();
ab14c7aa 6843
cfa3b256 6844 int tabCount = tabArray.GetCount();
41a85215 6845
cfa3b256 6846 for (int i = 0; i < tabCount; ++i)
61399247 6847 {
cfa3b256
JS
6848 int pos = tabArray[i];
6849 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6850 tabArray[i] = pos;
7f0d9d71 6851 }
41a85215 6852
cfa3b256 6853 int nextTabPos = -1;
61399247 6854
ab14c7aa
JS
6855 while (stringChunk.Find(wxT('\t')) >= 0)
6856 {
109bfc88
JS
6857 int absoluteWidth = 0;
6858
ab14c7aa 6859 // the string has a tab
7f0d9d71
JS
6860 // break up the string at the Tab
6861 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6862 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6863
31778480
JS
6864 if (partialExtents)
6865 {
109bfc88
JS
6866 int oldWidth;
6867 if (partialExtents->GetCount() > 0)
6868 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6869 else
6870 oldWidth = 0;
6871
31778480
JS
6872 // Add these partial extents
6873 wxArrayInt p;
6874 dc.GetPartialTextExtents(stringFragment, p);
6875 size_t j;
6876 for (j = 0; j < p.GetCount(); j++)
6877 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6878
6879 if (partialExtents->GetCount() > 0)
925a662a 6880 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6881 else
925a662a 6882 absoluteWidth = relativeX;
109bfc88
JS
6883 }
6884 else
6885 {
6886 dc.GetTextExtent(stringFragment, & w, & h);
6887 width += w;
603f702b 6888 absoluteWidth = width + relativeX;
109bfc88 6889 haveDescent = true;
31778480
JS
6890 }
6891
cfa3b256
JS
6892 bool notFound = true;
6893 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6894 {
cfa3b256 6895 nextTabPos = tabArray.Item(i);
4794d69c
JS
6896
6897 // Find the next tab position.
6898 // Even if we're at the end of the tab array, we must still process the chunk.
6899
6900 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6901 {
4794d69c
JS
6902 if (nextTabPos <= absoluteWidth)
6903 {
6904 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6905 nextTabPos = absoluteWidth + defaultTabWidth;
6906 }
6907
cfa3b256 6908 notFound = false;
925a662a 6909 width = nextTabPos - relativeX;
31778480
JS
6910
6911 if (partialExtents)
6912 partialExtents->Add(width);
7f0d9d71
JS
6913 }
6914 }
6915 }
6916 }
30bf7630 6917
31778480
JS
6918 if (!stringChunk.IsEmpty())
6919 {
31778480
JS
6920 if (partialExtents)
6921 {
109bfc88
JS
6922 int oldWidth;
6923 if (partialExtents->GetCount() > 0)
6924 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6925 else
6926 oldWidth = 0;
6927
31778480
JS
6928 // Add these partial extents
6929 wxArrayInt p;
6930 dc.GetPartialTextExtents(stringChunk, p);
6931 size_t j;
6932 for (j = 0; j < p.GetCount(); j++)
6933 partialExtents->Add(oldWidth + p[j]);
6934 }
109bfc88
JS
6935 else
6936 {
6937 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6938 width += w;
6939 haveDescent = true;
6940 }
6941 }
6942
6943 if (partialExtents)
6944 {
6945 int charHeight = dc.GetCharHeight();
6946 if ((*partialExtents).GetCount() > 0)
6947 w = (*partialExtents)[partialExtents->GetCount()-1];
6948 else
6949 w = 0;
6950 size = wxSize(w, charHeight);
6951 }
6952 else
6953 {
6954 size = wxSize(width, dc.GetCharHeight());
31778480 6955 }
30bf7630 6956
109bfc88
JS
6957 if (!haveDescent)
6958 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6959
30bf7630
JS
6960 if ( bScript )
6961 dc.SetFont(font);
6962
5d7836c4
JS
6963 return true;
6964}
6965
6966/// Do a split, returning an object containing the second part, and setting
6967/// the first part in 'this'.
6968wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6969{
ff76711f 6970 long index = pos - GetRange().GetStart();
3e541562 6971
28f92d74 6972 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6973 return NULL;
6974
6975 wxString firstPart = m_text.Mid(0, index);
6976 wxString secondPart = m_text.Mid(index);
6977
6978 m_text = firstPart;
6979
6980 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6981 newObject->SetAttributes(GetAttributes());
8db2e3ef 6982 newObject->SetProperties(GetProperties());
5d7836c4
JS
6983
6984 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6985 GetRange().SetEnd(pos-1);
3e541562 6986
5d7836c4
JS
6987 return newObject;
6988}
6989
6990/// Calculate range
6991void wxRichTextPlainText::CalculateRange(long start, long& end)
6992{
28f92d74 6993 end = start + m_text.length() - 1;
5d7836c4
JS
6994 m_range.SetRange(start, end);
6995}
6996
6997/// Delete range
6998bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6999{
7000 wxRichTextRange r = range;
7001
7002 r.LimitTo(GetRange());
7003
7004 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
7005 {
7006 m_text.Empty();
7007 return true;
7008 }
7009
7010 long startIndex = r.GetStart() - GetRange().GetStart();
7011 long len = r.GetLength();
7012
7013 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
7014 return true;
7015}
7016
7017/// Get text for the given range.
7018wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
7019{
7020 wxRichTextRange r = range;
7021
7022 r.LimitTo(GetRange());
7023
7024 long startIndex = r.GetStart() - GetRange().GetStart();
7025 long len = r.GetLength();
7026
7027 return m_text.Mid(startIndex, len);
7028}
7029
7030/// Returns true if this object can merge itself with the given one.
f7667b84 7031bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
5d7836c4 7032{
f7667b84
JS
7033 // JACS 2013-01-27
7034 if (!context.GetVirtualAttributesEnabled())
7035 {
7036 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
7037 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
7038 }
7039 else
7040 {
7041 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7042 if (!otherObj || m_text.empty())
7043 return false;
7044
7045 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7046 return false;
7047
7048 // Check if differing virtual attributes makes it impossible to merge
7049 // these strings.
7050
7051 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7052 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7053 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7054 return true;
7055 else if (hasVirtualAttr1 != hasVirtualAttr2)
7056 return false;
7057 else
7058 {
7059 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7060 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7061 return virtualAttr1 == virtualAttr2;
7062 }
7063 }
5d7836c4
JS
7064}
7065
7066/// Returns true if this object merged itself with the given one.
7067/// The calling code will then delete the given object.
f7667b84 7068bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
5d7836c4
JS
7069{
7070 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7071 wxASSERT( textObject != NULL );
7072
7073 if (textObject)
7074 {
7075 m_text += textObject->GetText();
99404ab0 7076 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
7077 return true;
7078 }
7079 else
7080 return false;
7081}
7082
f7667b84
JS
7083bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7084{
7085 // If this object has any virtual attributes at all, whether for the whole object
7086 // or individual ones, we should try splitting it by calling Split.
7087 // Must be more than one character in order to be able to split.
7088 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7089}
7090
7091wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7092{
7093 int count = context.GetVirtualSubobjectAttributesCount(this);
7094 if (count > 0 && GetParent())
7095 {
7096 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7097 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7098 if (node)
7099 {
7100 const wxRichTextAttr emptyAttr;
7101 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7102
7103 wxArrayInt positions;
7104 wxRichTextAttrArray attributes;
7105 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7106 {
7107 wxASSERT(positions.GetCount() == attributes.GetCount());
7108
7109 // We will gather up runs of text with the same virtual attributes
7110
7111 int len = m_text.Length();
7112 int i = 0;
7113
7114 // runStart and runEnd represent the accumulated run with a consistent attribute
7115 // that hasn't yet been appended
7116 int runStart = -1;
7117 int runEnd = -1;
7118 wxRichTextAttr currentAttr;
7119 wxString text = m_text;
7120 wxRichTextPlainText* lastPlainText = this;
7121
7122 for (i = 0; i < (int) positions.GetCount(); i++)
7123 {
7124 int pos = positions[i];
7125 wxASSERT(pos >= 0 && pos < len);
7126 if (pos >= 0 && pos < len)
7127 {
7128 const wxRichTextAttr& attr = attributes[i];
7129
7130 if (pos == 0)
7131 {
7132 runStart = 0;
7133 currentAttr = attr;
7134 }
7135 // Check if there was a gap from the last known attribute and this.
7136 // In that case, we need to do something with the span of non-attributed text.
7137 else if ((pos-1) > runEnd)
7138 {
7139 if (runEnd == -1)
7140 {
7141 // We hadn't processed anything previously, so the previous run is from the text start
7142 // to just before this position. The current attribute remains empty.
7143 runStart = 0;
7144 runEnd = pos-1;
7145 }
7146 else
7147 {
7148 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7149 // then just extend the run.
7150 if (currentAttr.IsDefault())
7151 {
7152 runEnd = pos-1;
7153 }
7154 else
7155 {
7156 // We need to add an object, or reuse the existing one.
7157 if (runStart == 0)
7158 {
7159 lastPlainText = this;
7160 SetText(text.Mid(runStart, runEnd - runStart + 1));
7161 }
7162 else
7163 {
7164 wxRichTextPlainText* obj = new wxRichTextPlainText;
7165 lastPlainText = obj;
7166 obj->SetAttributes(GetAttributes());
7167 obj->SetProperties(GetProperties());
7168 obj->SetParent(parent);
7169
7170 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7171 if (next)
7172 parent->GetChildren().Insert(next, obj);
7173 else
7174 parent->GetChildren().Append(obj);
7175 }
7176
7177 runStart = runEnd+1;
7178 runEnd = pos-1;
7179
7180 currentAttr = emptyAttr;
7181 }
7182 }
7183 }
7184
7185 wxASSERT(runEnd == pos-1);
7186
7187 // Now we only have to deal with the previous run
7188 if (currentAttr == attr)
7189 {
7190 // If we still have the same attributes, then we
7191 // simply increase the run size.
7192 runEnd = pos;
7193 }
7194 else
7195 {
7196 if (runEnd >= 0)
7197 {
7198 // We need to add an object, or reuse the existing one.
7199 if (runStart == 0)
7200 {
7201 lastPlainText = this;
7202 SetText(text.Mid(runStart, runEnd - runStart + 1));
7203 }
7204 else
7205 {
7206 wxRichTextPlainText* obj = new wxRichTextPlainText;
7207 lastPlainText = obj;
7208 obj->SetAttributes(GetAttributes());
7209 obj->SetProperties(GetProperties());
7210 obj->SetParent(parent);
7211
7212 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7213 if (next)
7214 parent->GetChildren().Insert(next, obj);
7215 else
7216 parent->GetChildren().Append(obj);
7217 }
7218 }
7219
7220 runStart = pos;
7221 runEnd = pos;
7222
7223 currentAttr = attr;
7224 }
7225 }
7226 }
7227
7228 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7229 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7230 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7231 {
7232 // If the current attribute is empty, merge the run with the next fragment
7233 // which by definition (because it's not specified) has empty attributes.
7234 if (currentAttr.IsDefault())
7235 runEnd = (len-1);
7236
7237 if (runEnd < (len-1))
7238 {
7239 // We need to add an object, or reuse the existing one.
7240 if (runStart == 0)
7241 {
7242 lastPlainText = this;
7243 SetText(text.Mid(runStart, runEnd - runStart + 1));
7244 }
7245 else
7246 {
7247 wxRichTextPlainText* obj = new wxRichTextPlainText;
7248 lastPlainText = obj;
7249 obj->SetAttributes(GetAttributes());
7250 obj->SetProperties(GetProperties());
7251 obj->SetParent(parent);
7252
7253 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7254 if (next)
7255 parent->GetChildren().Insert(next, obj);
7256 else
7257 parent->GetChildren().Append(obj);
7258 }
7259
7260 runStart = runEnd+1;
7261 runEnd = (len-1);
7262 }
7263
7264 // Now the last, non-attributed fragment at the end, if any
7265 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7266 {
7267 wxASSERT(runStart != 0);
7268
7269 wxRichTextPlainText* obj = new wxRichTextPlainText;
7270 obj->SetAttributes(GetAttributes());
7271 obj->SetProperties(GetProperties());
7272 obj->SetParent(parent);
7273
7274 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7275 if (next)
7276 parent->GetChildren().Insert(next, obj);
7277 else
7278 parent->GetChildren().Append(obj);
7279
7280 lastPlainText = obj;
7281 }
7282 }
7283
7284 return lastPlainText;
7285 }
7286 }
7287 }
7288 return this;
7289}
7290
5d7836c4
JS
7291/// Dump to output stream for debugging
7292void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7293{
7294 wxRichTextObject::Dump(stream);
7295 stream << m_text << wxT("\n");
7296}
7297
ff76711f
JS
7298/// Get the first position from pos that has a line break character.
7299long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7300{
7301 int i;
7302 int len = m_text.length();
7303 int startPos = pos - m_range.GetStart();
7304 for (i = startPos; i < len; i++)
7305 {
7306 wxChar ch = m_text[i];
7307 if (ch == wxRichTextLineBreakChar)
7308 {
7309 return i + m_range.GetStart();
7310 }
7311 }
7312 return -1;
7313}
7314
5d7836c4
JS
7315/*!
7316 * wxRichTextBuffer
7317 * This is a kind of box, used to represent the whole buffer
7318 */
7319
7320IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7321
7c9fdebe
JS
7322wxList wxRichTextBuffer::sm_handlers;
7323wxList wxRichTextBuffer::sm_drawingHandlers;
7324wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7325wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7326int wxRichTextBuffer::sm_bulletRightMargin = 20;
7327float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
e12b91a3 7328bool wxRichTextBuffer::sm_floatingLayoutMode = true;
5d7836c4
JS
7329
7330/// Initialisation
7331void wxRichTextBuffer::Init()
7332{
7333 m_commandProcessor = new wxCommandProcessor;
7334 m_styleSheet = NULL;
7335 m_modified = false;
7336 m_batchedCommandDepth = 0;
7337 m_batchedCommand = NULL;
7338 m_suppressUndo = 0;
d2d0adc7 7339 m_handlerFlags = 0;
44219ff0 7340 m_scale = 1.0;
32423dd8
JS
7341 m_dimensionScale = 1.0;
7342 m_fontScale = 1.0;
f819ed5d 7343 SetMargins(4);
5d7836c4
JS
7344}
7345
7346/// Initialisation
7347wxRichTextBuffer::~wxRichTextBuffer()
7348{
7349 delete m_commandProcessor;
7350 delete m_batchedCommand;
7351
7352 ClearStyleStack();
d2d0adc7 7353 ClearEventHandlers();
5d7836c4
JS
7354}
7355
85d8909b 7356void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 7357{
85d8909b 7358 Reset();
3e541562 7359
5d7836c4 7360 GetCommandProcessor()->ClearCommands();
5d7836c4 7361
5d7836c4 7362 Modify(false);
1e967276 7363 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
7364}
7365
0ca07313
JS
7366void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7367{
7368 wxRichTextParagraphLayoutBox::Copy(obj);
7369
7370 m_styleSheet = obj.m_styleSheet;
7371 m_modified = obj.m_modified;
bec80f4f
JS
7372 m_batchedCommandDepth = 0;
7373 if (m_batchedCommand)
7374 delete m_batchedCommand;
7375 m_batchedCommand = NULL;
0ca07313 7376 m_suppressUndo = obj.m_suppressUndo;
603f702b 7377 m_invalidRange = obj.m_invalidRange;
32423dd8
JS
7378 m_dimensionScale = obj.m_dimensionScale;
7379 m_fontScale = obj.m_fontScale;
0ca07313
JS
7380}
7381
38f833b1
JS
7382/// Push style sheet to top of stack
7383bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7384{
7385 if (m_styleSheet)
7386 styleSheet->InsertSheet(m_styleSheet);
7387
7388 SetStyleSheet(styleSheet);
41a85215 7389
38f833b1
JS
7390 return true;
7391}
7392
7393/// Pop style sheet from top of stack
7394wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7395{
7396 if (m_styleSheet)
7397 {
7398 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7399 m_styleSheet = oldSheet->GetNextSheet();
7400 oldSheet->Unlink();
41a85215 7401
38f833b1
JS
7402 return oldSheet;
7403 }
7404 else
7405 return NULL;
7406}
7407
0ca07313
JS
7408/// Submit command to insert paragraphs
7409bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7410{
4e63bfb9 7411 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
7412}
7413
7414/// Submit command to insert paragraphs
4e63bfb9 7415bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
7416{
7417 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 7418
0ca07313 7419 action->GetNewParagraphs() = paragraphs;
59509217 7420
0ca07313
JS
7421 action->SetPosition(pos);
7422
603f702b 7423 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
7424 if (!paragraphs.GetPartialParagraph())
7425 range.SetEnd(range.GetEnd()+1);
7426
0ca07313 7427 // Set the range we'll need to delete in Undo
99404ab0 7428 action->SetRange(range);
0ca07313 7429
603f702b 7430 buffer->SubmitAction(action);
0ca07313
JS
7431
7432 return true;
7433}
7434
5d7836c4 7435/// Submit command to insert the given text
fe5aa22c 7436bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7437{
4e63bfb9 7438 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
7439}
7440
7441/// Submit command to insert the given text
4e63bfb9 7442bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7443{
7444 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7445
24777478
JS
7446 wxRichTextAttr* p = NULL;
7447 wxRichTextAttr paraAttr;
fe5aa22c
JS
7448 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7449 {
7c081bd2 7450 // Get appropriate paragraph style
603f702b 7451 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
7452 if (!paraAttr.IsDefault())
7453 p = & paraAttr;
7454 }
7455
fe5aa22c 7456 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 7457
603f702b 7458 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 7459
6636ef8d 7460 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
7461 {
7462 // Don't count the newline when undoing
7463 length --;
5d7836c4 7464 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 7465 }
6636ef8d 7466 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 7467 length --;
5d7836c4
JS
7468
7469 action->SetPosition(pos);
7470
7471 // Set the range we'll need to delete in Undo
0ca07313 7472 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 7473
603f702b 7474 buffer->SubmitAction(action);
7fe8059f 7475
5d7836c4
JS
7476 return true;
7477}
7478
7479/// Submit command to insert the given text
fe5aa22c 7480bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7481{
4e63bfb9 7482 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
7483}
7484
7485/// Submit command to insert the given text
4e63bfb9 7486bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7487{
7488 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7489
24777478
JS
7490 wxRichTextAttr* p = NULL;
7491 wxRichTextAttr paraAttr;
fe5aa22c
JS
7492 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7493 {
603f702b 7494 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
7495 if (!paraAttr.IsDefault())
7496 p = & paraAttr;
7497 }
7498
603f702b 7499 wxRichTextAttr attr(buffer->GetDefaultStyle());
32423dd8
JS
7500 // Don't include box attributes such as margins
7501 attr.GetTextBoxAttr().Reset();
7fe8059f
WS
7502
7503 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
7504 action->GetNewParagraphs().AppendChild(newPara);
7505 action->GetNewParagraphs().UpdateRanges();
7506 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
7507 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7508 long pos1 = pos;
7509
6c0ea513
JS
7510 if (p)
7511 newPara->SetAttributes(*p);
7512
c025e094
JS
7513 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7514 {
7515 if (para && para->GetRange().GetEnd() == pos)
7516 pos1 ++;
e2d0875a
JS
7517
7518 // Now see if we need to number the paragraph.
6c0ea513 7519 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
7520 {
7521 wxRichTextAttr numberingAttr;
7522 if (FindNextParagraphNumber(para, numberingAttr))
7523 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7524 }
c025e094
JS
7525 }
7526
5d7836c4
JS
7527 action->SetPosition(pos);
7528
99404ab0 7529 // Use the default character style
603f702b 7530 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
7531 {
7532 // Check whether the default style merely reflects the paragraph/basic style,
7533 // in which case don't apply it.
603f702b 7534 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
32423dd8 7535 defaultStyle.GetTextBoxAttr().Reset();
24777478 7536 wxRichTextAttr toApply;
c025e094
JS
7537 if (para)
7538 {
603f702b 7539 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 7540 wxRichTextAttr newAttr;
c025e094
JS
7541 // This filters out attributes that are accounted for by the current
7542 // paragraph/basic style
7543 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7544 }
7545 else
7546 toApply = defaultStyle;
7547
7548 if (!toApply.IsDefault())
7549 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7550 }
99404ab0 7551
5d7836c4 7552 // Set the range we'll need to delete in Undo
c025e094 7553 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 7554
603f702b 7555 buffer->SubmitAction(action);
7fe8059f 7556
5d7836c4
JS
7557 return true;
7558}
7559
7560/// Submit command to insert the given image
24777478
JS
7561bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7562 const wxRichTextAttr& textAttr)
5d7836c4 7563{
4e63bfb9 7564 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
7565}
7566
7567/// Submit command to insert the given image
4e63bfb9
JS
7568bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7569 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
7570 const wxRichTextAttr& textAttr)
7571{
7572 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7573
24777478
JS
7574 wxRichTextAttr* p = NULL;
7575 wxRichTextAttr paraAttr;
fe5aa22c
JS
7576 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7577 {
603f702b 7578 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
7579 if (!paraAttr.IsDefault())
7580 p = & paraAttr;
7581 }
7582
603f702b 7583 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 7584
32423dd8
JS
7585 // Don't include box attributes such as margins
7586 attr.GetTextBoxAttr().Reset();
7587
5d7836c4 7588 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
7589 if (p)
7590 newPara->SetAttributes(*p);
7591
5d7836c4
JS
7592 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7593 newPara->AppendChild(imageObject);
24777478 7594 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7595 action->GetNewParagraphs().AppendChild(newPara);
7596 action->GetNewParagraphs().UpdateRanges();
7597
7598 action->GetNewParagraphs().SetPartialParagraph(true);
7599
7600 action->SetPosition(pos);
7601
7602 // Set the range we'll need to delete in Undo
7603 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7604
603f702b 7605 buffer->SubmitAction(action);
7fe8059f 7606
5d7836c4
JS
7607 return true;
7608}
7609
cdaed652 7610// Insert an object with no change of it
603f702b
JS
7611wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7612{
4e63bfb9 7613 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7614}
7615
7616// Insert an object with no change of it
4e63bfb9 7617wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7618{
603f702b 7619 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7620
24777478
JS
7621 wxRichTextAttr* p = NULL;
7622 wxRichTextAttr paraAttr;
cdaed652
VZ
7623 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7624 {
603f702b 7625 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7626 if (!paraAttr.IsDefault())
7627 p = & paraAttr;
7628 }
7629
603f702b 7630 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652 7631
32423dd8
JS
7632 // Don't include box attributes such as margins
7633 attr.GetTextBoxAttr().Reset();
7634
cdaed652
VZ
7635 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7636 if (p)
7637 newPara->SetAttributes(*p);
7638
7639 newPara->AppendChild(object);
7640 action->GetNewParagraphs().AppendChild(newPara);
7641 action->GetNewParagraphs().UpdateRanges();
7642
7643 action->GetNewParagraphs().SetPartialParagraph(true);
7644
7645 action->SetPosition(pos);
7646
7647 // Set the range we'll need to delete in Undo
7648 action->SetRange(wxRichTextRange(pos, pos));
7649
603f702b 7650 buffer->SubmitAction(action);
cdaed652 7651
603f702b
JS
7652 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7653 return obj;
cdaed652 7654}
603f702b 7655
7c9fdebe
JS
7656wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7657 const wxRichTextProperties& properties,
7658 wxRichTextCtrl* ctrl, int flags,
7659 const wxRichTextAttr& textAttr)
7660{
7661 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7662
7663 wxRichTextAttr* p = NULL;
7664 wxRichTextAttr paraAttr;
7665 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7666 {
7667 paraAttr = GetStyleForNewParagraph(buffer, pos);
7668 if (!paraAttr.IsDefault())
7669 p = & paraAttr;
7670 }
7671
7672 wxRichTextAttr attr(buffer->GetDefaultStyle());
7673
32423dd8
JS
7674 // Don't include box attributes such as margins
7675 attr.GetTextBoxAttr().Reset();
7676
7c9fdebe
JS
7677 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7678 if (p)
7679 newPara->SetAttributes(*p);
7680
7681 wxRichTextField* fieldObject = new wxRichTextField();
7682 fieldObject->wxRichTextObject::SetProperties(properties);
7683 fieldObject->SetFieldType(fieldType);
7684 fieldObject->SetAttributes(textAttr);
7685 newPara->AppendChild(fieldObject);
7686 action->GetNewParagraphs().AppendChild(newPara);
7687 action->GetNewParagraphs().UpdateRanges();
7688 action->GetNewParagraphs().SetPartialParagraph(true);
7689 action->SetPosition(pos);
7690
7691 // Set the range we'll need to delete in Undo
7692 action->SetRange(wxRichTextRange(pos, pos));
7693
7694 buffer->SubmitAction(action);
7695
7696 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7697 return obj;
7698}
7699
fe5aa22c
JS
7700/// Get the style that is appropriate for a new paragraph at this position.
7701/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7702/// style.
603f702b 7703wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7704{
7705 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7706 if (para)
7707 {
24777478 7708 wxRichTextAttr attr;
d2d0adc7 7709 bool foundAttributes = false;
3e541562 7710
d2d0adc7 7711 // Look for a matching paragraph style
603f702b 7712 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7713 {
603f702b 7714 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7715 if (paraDef)
fe5aa22c 7716 {
caad0109
JS
7717 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7718 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7719 {
603f702b 7720 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7721 if (nextParaDef)
7722 {
7723 foundAttributes = true;
603f702b 7724 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7725 }
7726 }
3e541562 7727
d2d0adc7
JS
7728 // If we didn't find the 'next style', use this style instead.
7729 if (!foundAttributes)
7730 {
7731 foundAttributes = true;
603f702b 7732 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7733 }
fe5aa22c
JS
7734 }
7735 }
e2d0875a
JS
7736
7737 // Also apply list style if present
603f702b 7738 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7739 {
603f702b 7740 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7741 if (listDef)
7742 {
7743 int thisIndent = para->GetAttributes().GetLeftIndent();
7744 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7745
7746 // Apply the overall list style, and item style for this level
603f702b 7747 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7748 wxRichTextApplyStyle(attr, listStyle);
7749 attr.SetOutlineLevel(thisLevel);
7750 if (para->GetAttributes().HasBulletNumber())
7751 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7752 }
34b4899d 7753 }
e2d0875a 7754
d2d0adc7
JS
7755 if (!foundAttributes)
7756 {
7757 attr = para->GetAttributes();
7758 int flags = attr.GetFlags();
fe5aa22c 7759
d2d0adc7
JS
7760 // Eliminate character styles
7761 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7762 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7763 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7764 attr.SetFlags(flags);
7765 }
3e541562 7766
fe5aa22c
JS
7767 return attr;
7768 }
7769 else
24777478 7770 return wxRichTextAttr();
fe5aa22c
JS
7771}
7772
5d7836c4 7773/// Submit command to delete this range
12cc29c5 7774bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7775{
603f702b
JS
7776 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7777}
7778
7779/// Submit command to delete this range
7780bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7781{
7782 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7783
12cc29c5 7784 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7785
7786 // Set the range to delete
7787 action->SetRange(range);
7fe8059f 7788
5d7836c4
JS
7789 // Copy the fragment that we'll need to restore in Undo
7790 CopyFragment(range, action->GetOldParagraphs());
7791
6c0ea513
JS
7792 // See if we're deleting a paragraph marker, in which case we need to
7793 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7794 if (range.GetStart() == range.GetEnd())
5d7836c4 7795 {
6c0ea513
JS
7796 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7797 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7798 {
6c0ea513
JS
7799 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7800 if (nextPara && nextPara != para)
5d7836c4 7801 {
6c0ea513
JS
7802 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7803 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7804 }
7805 }
7806 }
7807
603f702b 7808 buffer->SubmitAction(action);
7fe8059f 7809
5d7836c4
JS
7810 return true;
7811}
7812
7813/// Collapse undo/redo commands
7814bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7815{
7816 if (m_batchedCommandDepth == 0)
7817 {
7818 wxASSERT(m_batchedCommand == NULL);
7819 if (m_batchedCommand)
7820 {
0745f364 7821 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7822 }
7823 m_batchedCommand = new wxRichTextCommand(cmdName);
7824 }
7825
7fe8059f 7826 m_batchedCommandDepth ++;
5d7836c4
JS
7827
7828 return true;
7829}
7830
7831/// Collapse undo/redo commands
7832bool wxRichTextBuffer::EndBatchUndo()
7833{
7834 m_batchedCommandDepth --;
7835
7836 wxASSERT(m_batchedCommandDepth >= 0);
7837 wxASSERT(m_batchedCommand != NULL);
7838
7839 if (m_batchedCommandDepth == 0)
7840 {
0745f364 7841 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7842 m_batchedCommand = NULL;
7843 }
7844
7845 return true;
7846}
7847
7848/// Submit immediately, or delay according to whether collapsing is on
7849bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7850{
cc2aecde
JS
7851 if (action && !action->GetNewParagraphs().IsEmpty())
7852 PrepareContent(action->GetNewParagraphs());
7853
5d7836c4 7854 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7855 {
7856 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7857 cmd->AddAction(action);
7858 cmd->Do();
7859 cmd->GetActions().Clear();
7860 delete cmd;
7861
5d7836c4 7862 m_batchedCommand->AddAction(action);
0745f364 7863 }
5d7836c4
JS
7864 else
7865 {
7866 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7867 cmd->AddAction(action);
7868
7869 // Only store it if we're not suppressing undo.
7870 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7871 }
7872
7873 return true;
7874}
7875
7876/// Begin suppressing undo/redo commands.
7877bool wxRichTextBuffer::BeginSuppressUndo()
7878{
7fe8059f 7879 m_suppressUndo ++;
5d7836c4
JS
7880
7881 return true;
7882}
7883
7884/// End suppressing undo/redo commands.
7885bool wxRichTextBuffer::EndSuppressUndo()
7886{
7fe8059f 7887 m_suppressUndo --;
5d7836c4
JS
7888
7889 return true;
7890}
7891
7892/// Begin using a style
24777478 7893bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7894{
24777478 7895 wxRichTextAttr newStyle(GetDefaultStyle());
32423dd8 7896 newStyle.GetTextBoxAttr().Reset();
5d7836c4
JS
7897
7898 // Save the old default style
32423dd8 7899 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
5d7836c4
JS
7900
7901 wxRichTextApplyStyle(newStyle, style);
7902 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7903
7904 SetDefaultStyle(newStyle);
7905
5d7836c4
JS
7906 return true;
7907}
7908
7909/// End the style
7910bool wxRichTextBuffer::EndStyle()
7911{
63886f6d 7912 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7913 {
7914 wxLogDebug(_("Too many EndStyle calls!"));
7915 return false;
7916 }
7917
09f14108 7918 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7919 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7920 m_attributeStack.Erase(node);
5d7836c4
JS
7921
7922 SetDefaultStyle(*attr);
7923
7924 delete attr;
7925 return true;
7926}
7927
7928/// End all styles
7929bool wxRichTextBuffer::EndAllStyles()
7930{
7931 while (m_attributeStack.GetCount() != 0)
7932 EndStyle();
7933 return true;
7934}
7935
7936/// Clear the style stack
7937void wxRichTextBuffer::ClearStyleStack()
7938{
09f14108 7939 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7940 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7941 m_attributeStack.Clear();
7942}
7943
7944/// Begin using bold
7945bool wxRichTextBuffer::BeginBold()
7946{
24777478 7947 wxRichTextAttr attr;
7d76fbd5 7948 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7949
5d7836c4
JS
7950 return BeginStyle(attr);
7951}
7952
7953/// Begin using italic
7954bool wxRichTextBuffer::BeginItalic()
7955{
24777478 7956 wxRichTextAttr attr;
7d76fbd5 7957 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7958
5d7836c4
JS
7959 return BeginStyle(attr);
7960}
7961
7962/// Begin using underline
7963bool wxRichTextBuffer::BeginUnderline()
7964{
24777478 7965 wxRichTextAttr attr;
44cc96a8 7966 attr.SetFontUnderlined(true);
7fe8059f 7967
5d7836c4
JS
7968 return BeginStyle(attr);
7969}
7970
7971/// Begin using point size
7972bool wxRichTextBuffer::BeginFontSize(int pointSize)
7973{
24777478 7974 wxRichTextAttr attr;
44cc96a8 7975 attr.SetFontSize(pointSize);
7fe8059f 7976
5d7836c4
JS
7977 return BeginStyle(attr);
7978}
7979
7980/// Begin using this font
7981bool wxRichTextBuffer::BeginFont(const wxFont& font)
7982{
24777478 7983 wxRichTextAttr attr;
5d7836c4 7984 attr.SetFont(font);
7fe8059f 7985
5d7836c4
JS
7986 return BeginStyle(attr);
7987}
7988
7989/// Begin using this colour
7990bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7991{
24777478 7992 wxRichTextAttr attr;
5d7836c4
JS
7993 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7994 attr.SetTextColour(colour);
7fe8059f 7995
5d7836c4
JS
7996 return BeginStyle(attr);
7997}
7998
7999/// Begin using alignment
8000bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
8001{
24777478 8002 wxRichTextAttr attr;
5d7836c4
JS
8003 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
8004 attr.SetAlignment(alignment);
7fe8059f 8005
5d7836c4
JS
8006 return BeginStyle(attr);
8007}
8008
8009/// Begin left indent
8010bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
8011{
24777478 8012 wxRichTextAttr attr;
5d7836c4
JS
8013 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
8014 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8015
5d7836c4
JS
8016 return BeginStyle(attr);
8017}
8018
8019/// Begin right indent
8020bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
8021{
24777478 8022 wxRichTextAttr attr;
5d7836c4
JS
8023 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
8024 attr.SetRightIndent(rightIndent);
7fe8059f 8025
5d7836c4
JS
8026 return BeginStyle(attr);
8027}
8028
8029/// Begin paragraph spacing
8030bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
8031{
8032 long flags = 0;
8033 if (before != 0)
8034 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
8035 if (after != 0)
8036 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
8037
24777478 8038 wxRichTextAttr attr;
5d7836c4
JS
8039 attr.SetFlags(flags);
8040 attr.SetParagraphSpacingBefore(before);
8041 attr.SetParagraphSpacingAfter(after);
7fe8059f 8042
5d7836c4
JS
8043 return BeginStyle(attr);
8044}
8045
8046/// Begin line spacing
8047bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8048{
24777478 8049 wxRichTextAttr attr;
5d7836c4
JS
8050 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8051 attr.SetLineSpacing(lineSpacing);
7fe8059f 8052
5d7836c4
JS
8053 return BeginStyle(attr);
8054}
8055
8056/// Begin numbered bullet
8057bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8058{
24777478 8059 wxRichTextAttr attr;
f089713f 8060 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8061 attr.SetBulletStyle(bulletStyle);
8062 attr.SetBulletNumber(bulletNumber);
8063 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 8064
5d7836c4
JS
8065 return BeginStyle(attr);
8066}
8067
8068/// Begin symbol bullet
d2d0adc7 8069bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 8070{
24777478 8071 wxRichTextAttr attr;
f089713f 8072 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
8073 attr.SetBulletStyle(bulletStyle);
8074 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 8075 attr.SetBulletText(symbol);
7fe8059f 8076
5d7836c4
JS
8077 return BeginStyle(attr);
8078}
8079
f089713f
JS
8080/// Begin standard bullet
8081bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8082{
24777478 8083 wxRichTextAttr attr;
f089713f
JS
8084 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8085 attr.SetBulletStyle(bulletStyle);
8086 attr.SetLeftIndent(leftIndent, leftSubIndent);
8087 attr.SetBulletName(bulletName);
8088
8089 return BeginStyle(attr);
8090}
8091
5d7836c4
JS
8092/// Begin named character style
8093bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8094{
8095 if (GetStyleSheet())
8096 {
8097 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8098 if (def)
8099 {
24777478 8100 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8101 return BeginStyle(attr);
8102 }
8103 }
8104 return false;
8105}
8106
8107/// Begin named paragraph style
8108bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8109{
8110 if (GetStyleSheet())
8111 {
8112 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8113 if (def)
8114 {
24777478 8115 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
8116 return BeginStyle(attr);
8117 }
8118 }
8119 return false;
8120}
8121
f089713f
JS
8122/// Begin named list style
8123bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8124{
8125 if (GetStyleSheet())
8126 {
8127 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8128 if (def)
8129 {
24777478 8130 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
8131
8132 attr.SetBulletNumber(number);
8133
8134 return BeginStyle(attr);
8135 }
8136 }
8137 return false;
8138}
8139
d2d0adc7
JS
8140/// Begin URL
8141bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8142{
24777478 8143 wxRichTextAttr attr;
d2d0adc7
JS
8144
8145 if (!characterStyle.IsEmpty() && GetStyleSheet())
8146 {
8147 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8148 if (def)
8149 {
336d8ae9 8150 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
8151 }
8152 }
8153 attr.SetURL(url);
8154
8155 return BeginStyle(attr);
8156}
8157
5d7836c4
JS
8158/// Adds a handler to the end
8159void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8160{
8161 sm_handlers.Append(handler);
8162}
8163
8164/// Inserts a handler at the front
8165void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8166{
8167 sm_handlers.Insert( handler );
8168}
8169
8170/// Removes a handler
8171bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8172{
8173 wxRichTextFileHandler *handler = FindHandler(name);
8174 if (handler)
8175 {
8176 sm_handlers.DeleteObject(handler);
8177 delete handler;
8178 return true;
8179 }
8180 else
8181 return false;
8182}
8183
8184/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
8185wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8186 wxRichTextFileType imageType)
5d7836c4
JS
8187{
8188 if (imageType != wxRICHTEXT_TYPE_ANY)
8189 return FindHandler(imageType);
0ca07313 8190 else if (!filename.IsEmpty())
5d7836c4
JS
8191 {
8192 wxString path, file, ext;
a51e601e 8193 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
8194 return FindHandler(ext, imageType);
8195 }
0ca07313
JS
8196 else
8197 return NULL;
5d7836c4
JS
8198}
8199
8200
8201/// Finds a handler by name
8202wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8203{
8204 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8205 while (node)
8206 {
8207 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8208 if (handler->GetName().Lower() == name.Lower()) return handler;
8209
8210 node = node->GetNext();
8211 }
8212 return NULL;
8213}
8214
8215/// Finds a handler by extension and type
d75a69e8 8216wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
8217{
8218 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8219 while (node)
8220 {
8221 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8222 if ( handler->GetExtension().Lower() == extension.Lower() &&
8223 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8224 return handler;
8225 node = node->GetNext();
8226 }
8227 return 0;
8228}
8229
8230/// Finds a handler by type
d75a69e8 8231wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
8232{
8233 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8234 while (node)
8235 {
8236 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8237 if (handler->GetType() == type) return handler;
8238 node = node->GetNext();
8239 }
8240 return NULL;
8241}
8242
8243void wxRichTextBuffer::InitStandardHandlers()
8244{
8245 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8246 AddHandler(new wxRichTextPlainTextHandler);
8247}
8248
8249void wxRichTextBuffer::CleanUpHandlers()
8250{
8251 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8252 while (node)
8253 {
8254 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8255 wxList::compatibility_iterator next = node->GetNext();
8256 delete handler;
8257 node = next;
8258 }
8259
8260 sm_handlers.Clear();
8261}
8262
1e967276 8263wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 8264{
1e967276
JS
8265 if (types)
8266 types->Clear();
8267
5d7836c4
JS
8268 wxString wildcard;
8269
8270 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8271 int count = 0;
8272 while (node)
8273 {
8274 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 8275 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
8276 {
8277 if (combine)
8278 {
8279 if (count > 0)
8280 wildcard += wxT(";");
8281 wildcard += wxT("*.") + handler->GetExtension();
8282 }
8283 else
8284 {
8285 if (count > 0)
8286 wildcard += wxT("|");
8287 wildcard += handler->GetName();
8288 wildcard += wxT(" ");
8289 wildcard += _("files");
8290 wildcard += wxT(" (*.");
8291 wildcard += handler->GetExtension();
8292 wildcard += wxT(")|*.");
8293 wildcard += handler->GetExtension();
1e967276
JS
8294 if (types)
8295 types->Add(handler->GetType());
5d7836c4
JS
8296 }
8297 count ++;
8298 }
8299
8300 node = node->GetNext();
8301 }
8302
8303 if (combine)
8304 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8305 return wildcard;
8306}
8307
8308/// Load a file
d75a69e8 8309bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8310{
8311 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8312 if (handler)
1e967276 8313 {
24777478 8314 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8315 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8316 bool success = handler->LoadFile(this, filename);
8317 Invalidate(wxRICHTEXT_ALL);
8318 return success;
8319 }
5d7836c4
JS
8320 else
8321 return false;
8322}
8323
8324/// Save a file
d75a69e8 8325bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
8326{
8327 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8328 if (handler)
d2d0adc7
JS
8329 {
8330 handler->SetFlags(GetHandlerFlags());
5d7836c4 8331 return handler->SaveFile(this, filename);
d2d0adc7 8332 }
5d7836c4
JS
8333 else
8334 return false;
8335}
8336
8337/// Load from a stream
d75a69e8 8338bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8339{
8340 wxRichTextFileHandler* handler = FindHandler(type);
8341 if (handler)
1e967276 8342 {
24777478 8343 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 8344 handler->SetFlags(GetHandlerFlags());
1e967276
JS
8345 bool success = handler->LoadFile(this, stream);
8346 Invalidate(wxRICHTEXT_ALL);
8347 return success;
8348 }
5d7836c4
JS
8349 else
8350 return false;
8351}
8352
8353/// Save to a stream
d75a69e8 8354bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
8355{
8356 wxRichTextFileHandler* handler = FindHandler(type);
8357 if (handler)
d2d0adc7
JS
8358 {
8359 handler->SetFlags(GetHandlerFlags());
5d7836c4 8360 return handler->SaveFile(this, stream);
d2d0adc7 8361 }
5d7836c4
JS
8362 else
8363 return false;
8364}
8365
8366/// Copy the range to the clipboard
8367bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8368{
8369 bool success = false;
603f702b
JS
8370 wxRichTextParagraphLayoutBox* container = this;
8371 if (GetRichTextCtrl())
8372 container = GetRichTextCtrl()->GetFocusObject();
8373
11ef729d 8374#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 8375
d2142335 8376 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 8377 {
0ca07313
JS
8378 wxTheClipboard->Clear();
8379
8380 // Add composite object
8381
8382 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8383
8384 {
603f702b 8385 wxString text = container->GetTextForRange(range);
0ca07313
JS
8386
8387#ifdef __WXMSW__
8388 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8389#endif
8390
8391 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8392 }
8393
8394 // Add rich text buffer data object. This needs the XML handler to be present.
8395
8396 if (FindHandler(wxRICHTEXT_TYPE_XML))
8397 {
8398 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 8399 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
8400
8401 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8402 }
8403
8404 if (wxTheClipboard->SetData(compositeObject))
8405 success = true;
8406
5d7836c4
JS
8407 wxTheClipboard->Close();
8408 }
0ca07313 8409
39a1c2f2
WS
8410#else
8411 wxUnusedVar(range);
8412#endif
5d7836c4
JS
8413 return success;
8414}
8415
8416/// Paste the clipboard content to the buffer
8417bool wxRichTextBuffer::PasteFromClipboard(long position)
8418{
8419 bool success = false;
603f702b
JS
8420 wxRichTextParagraphLayoutBox* container = this;
8421 if (GetRichTextCtrl())
8422 container = GetRichTextCtrl()->GetFocusObject();
8423
11ef729d 8424#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
8425 if (CanPasteFromClipboard())
8426 {
8427 if (wxTheClipboard->Open())
8428 {
0ca07313
JS
8429 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8430 {
8431 wxRichTextBufferDataObject data;
8432 wxTheClipboard->GetData(data);
8433 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8434 if (richTextBuffer)
8435 {
4e63bfb9 8436 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 8437 if (GetRichTextCtrl())
603f702b 8438 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
8439 delete richTextBuffer;
8440 }
8441 }
f7d83f24 8442 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
8443 #if wxUSE_UNICODE
8444 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8445 #endif
f7d83f24 8446 )
5d7836c4
JS
8447 {
8448 wxTextDataObject data;
8449 wxTheClipboard->GetData(data);
8450 wxString text(data.GetText());
c21f3211
JS
8451#ifdef __WXMSW__
8452 wxString text2;
8453 text2.Alloc(text.Length()+1);
8454 size_t i;
8455 for (i = 0; i < text.Length(); i++)
8456 {
8457 wxChar ch = text[i];
8458 if (ch != wxT('\r'))
8459 text2 += ch;
8460 }
8461#else
8462 wxString text2 = text;
8463#endif
4e63bfb9 8464 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 8465
62381daa
JS
8466 if (GetRichTextCtrl())
8467 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8468
5d7836c4
JS
8469 success = true;
8470 }
8471 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8472 {
8473 wxBitmapDataObject data;
8474 wxTheClipboard->GetData(data);
8475 wxBitmap bitmap(data.GetBitmap());
8476 wxImage image(bitmap.ConvertToImage());
8477
603f702b 8478 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 8479
5d7836c4
JS
8480 action->GetNewParagraphs().AddImage(image);
8481
8482 if (action->GetNewParagraphs().GetChildCount() == 1)
8483 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 8484
9c8e10ad 8485 action->SetPosition(position+1);
7fe8059f 8486
5d7836c4 8487 // Set the range we'll need to delete in Undo
9c8e10ad 8488 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 8489
5d7836c4
JS
8490 SubmitAction(action);
8491
8492 success = true;
8493 }
8494 wxTheClipboard->Close();
8495 }
8496 }
39a1c2f2
WS
8497#else
8498 wxUnusedVar(position);
8499#endif
5d7836c4
JS
8500 return success;
8501}
8502
8503/// Can we paste from the clipboard?
8504bool wxRichTextBuffer::CanPasteFromClipboard() const
8505{
7fe8059f 8506 bool canPaste = false;
11ef729d 8507#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 8508 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 8509 {
f7d83f24
VZ
8510 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8511#if wxUSE_UNICODE
603f702b
JS
8512 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8513#endif
8514 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8515 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 8516 {
7fe8059f 8517 canPaste = true;
5d7836c4
JS
8518 }
8519 wxTheClipboard->Close();
8520 }
39a1c2f2 8521#endif
5d7836c4
JS
8522 return canPaste;
8523}
8524
8525/// Dumps contents of buffer for debugging purposes
8526void wxRichTextBuffer::Dump()
8527{
8528 wxString text;
8529 {
8530 wxStringOutputStream stream(& text);
8531 wxTextOutputStream textStream(stream);
8532 Dump(textStream);
8533 }
8534
8535 wxLogDebug(text);
8536}
8537
d2d0adc7
JS
8538/// Add an event handler
8539bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8540{
8541 m_eventHandlers.Append(handler);
8542 return true;
8543}
8544
8545/// Remove an event handler
8546bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8547{
8548 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8549 if (node)
8550 {
8551 m_eventHandlers.Erase(node);
8552 if (deleteHandler)
8553 delete handler;
3e541562 8554
d2d0adc7
JS
8555 return true;
8556 }
8557 else
8558 return false;
8559}
8560
8561/// Clear event handlers
8562void wxRichTextBuffer::ClearEventHandlers()
8563{
8564 m_eventHandlers.Clear();
8565}
8566
8567/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8568/// otherwise will stop at the first successful one.
8569bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8570{
8571 bool success = false;
8572 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8573 {
8574 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8575 if (handler->ProcessEvent(event))
8576 {
8577 success = true;
8578 if (!sendToAll)
8579 return true;
8580 }
8581 }
8582 return success;
8583}
8584
8585/// Set style sheet and notify of the change
8586bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8587{
8588 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 8589
616c7cbd 8590 wxWindowID winid = wxID_ANY;
d2d0adc7 8591 if (GetRichTextCtrl())
616c7cbd 8592 winid = GetRichTextCtrl()->GetId();
3e541562 8593
616c7cbd 8594 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 8595 event.SetEventObject(GetRichTextCtrl());
603f702b 8596 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
8597 event.SetOldStyleSheet(oldSheet);
8598 event.SetNewStyleSheet(sheet);
8599 event.Allow();
3e541562 8600
d2d0adc7
JS
8601 if (SendEvent(event) && !event.IsAllowed())
8602 {
8603 if (sheet != oldSheet)
8604 delete sheet;
8605
8606 return false;
8607 }
8608
8609 if (oldSheet && oldSheet != sheet)
8610 delete oldSheet;
8611
8612 SetStyleSheet(sheet);
8613
8614 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8615 event.SetOldStyleSheet(NULL);
8616 event.Allow();
8617
8618 return SendEvent(event);
8619}
8620
8621/// Set renderer, deleting old one
8622void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8623{
8624 if (sm_renderer)
8625 delete sm_renderer;
8626 sm_renderer = renderer;
8627}
8628
603f702b
JS
8629/// Hit-testing: returns a flag indicating hit test details, plus
8630/// information about position
8db2e3ef 8631int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 8632{
8db2e3ef 8633 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
8634 if (ret != wxRICHTEXT_HITTEST_NONE)
8635 {
8636 return ret;
8637 }
8638 else
8639 {
8640 textPosition = m_ownRange.GetEnd()-1;
8641 *obj = this;
8642 *contextObj = this;
8643 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8644 }
8645}
8646
32423dd8
JS
8647void wxRichTextBuffer::SetFontScale(double fontScale)
8648{
8649 m_fontScale = fontScale;
8650 m_fontTable.SetFontScale(fontScale);
8651}
8652
8653void wxRichTextBuffer::SetDimensionScale(double dimScale)
8654{
8655 m_dimensionScale = dimScale;
8656}
8657
24777478 8658bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8659{
a1b806b9 8660 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8661 {
ecb5fbf1
JS
8662 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8663 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8664 }
8665 else
8666 {
ecb5fbf1
JS
8667 wxCheckSetPen(dc, *wxBLACK_PEN);
8668 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8669 }
8670
8671 wxFont font;
44cc96a8
JS
8672 if (bulletAttr.HasFont())
8673 {
8674 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8675 }
d2d0adc7
JS
8676 else
8677 font = (*wxNORMAL_FONT);
8678
ecb5fbf1 8679 wxCheckSetFont(dc, font);
d2d0adc7
JS
8680
8681 int charHeight = dc.GetCharHeight();
3e541562 8682
d2d0adc7
JS
8683 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8684 int bulletHeight = bulletWidth;
8685
8686 int x = rect.x;
3e541562 8687
d2d0adc7
JS
8688 // Calculate the top position of the character (as opposed to the whole line height)
8689 int y = rect.y + (rect.height - charHeight);
3e541562 8690
d2d0adc7
JS
8691 // Calculate where the bullet should be positioned
8692 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8693
d2d0adc7 8694 // The margin between a bullet and text.
44219ff0 8695 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8696
d2d0adc7
JS
8697 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8698 x = rect.x + rect.width - bulletWidth - margin;
8699 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8700 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8701
d2d0adc7
JS
8702 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8703 {
8704 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8705 }
8706 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8707 {
8708 wxPoint pts[5];
8709 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8710 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8711 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8712 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8713
d2d0adc7
JS
8714 dc.DrawPolygon(4, pts);
8715 }
8716 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8717 {
8718 wxPoint pts[3];
8719 pts[0].x = x; pts[0].y = y;
8720 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8721 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8722
d2d0adc7
JS
8723 dc.DrawPolygon(3, pts);
8724 }
603f702b
JS
8725 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8726 {
8727 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8728 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8729 }
d2d0adc7
JS
8730 else // "standard/circle", and catch-all
8731 {
8732 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8733 }
8734
d2d0adc7
JS
8735 return true;
8736}
8737
24777478 8738bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8739{
8740 if (!text.empty())
8741 {
8742 wxFont font;
44cc96a8
JS
8743 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8744 {
24777478 8745 wxRichTextAttr fontAttr;
32423dd8
JS
8746 if (attr.HasFontPixelSize())
8747 fontAttr.SetFontPixelSize(attr.GetFontSize());
8748 else
8749 fontAttr.SetFontPointSize(attr.GetFontSize());
44cc96a8
JS
8750 fontAttr.SetFontStyle(attr.GetFontStyle());
8751 fontAttr.SetFontWeight(attr.GetFontWeight());
8752 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8753 fontAttr.SetFontFaceName(attr.GetBulletFont());
8754 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8755 }
8756 else if (attr.HasFont())
8757 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8758 else
8759 font = (*wxNORMAL_FONT);
8760
ecb5fbf1 8761 wxCheckSetFont(dc, font);
d2d0adc7 8762
a1b806b9 8763 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8764 dc.SetTextForeground(attr.GetTextColour());
8765
04ee05f9 8766 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8767
8768 int charHeight = dc.GetCharHeight();
8769 wxCoord tw, th;
8770 dc.GetTextExtent(text, & tw, & th);
8771
8772 int x = rect.x;
8773
8774 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8775 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8776
8777 // The margin between a bullet and text.
44219ff0 8778 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8779
d2d0adc7
JS
8780 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8781 x = (rect.x + rect.width) - tw - margin;
8782 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8783 x = x + (rect.width)/2 - tw/2;
8784
8785 dc.DrawText(text, x, y);
3e541562 8786
d2d0adc7
JS
8787 return true;
8788 }
8789 else
8790 return false;
8791}
8792
24777478 8793bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8794{
8795 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8796 // with the buffer. The store will allow retrieval from memory, disk or other means.
8797 return false;
8798}
8799
8800/// Enumerate the standard bullet names currently supported
8801bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8802{
04529b2a 8803 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8804 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8805 bulletNames.Add(wxTRANSLATE("standard/square"));
8806 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8807 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8808
8809 return true;
8810}
5d7836c4 8811
bec80f4f
JS
8812/*!
8813 * wxRichTextBox
8814 */
8815
603f702b 8816IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8817
8818wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8819 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8820{
8821}
8822
8823/// Draw the item
8db2e3ef 8824bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8825{
603f702b
JS
8826 if (!IsShown())
8827 return true;
5ad9ae3a 8828
603f702b
JS
8829 // TODO: if the active object in the control, draw an indication.
8830 // We need to add the concept of active object, and not just focus object,
8831 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8832 // Ultimately we would like to be able to interactively resize an active object
8833 // using drag handles.
8db2e3ef 8834 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8835}
5ad9ae3a 8836
603f702b
JS
8837/// Copy
8838void wxRichTextBox::Copy(const wxRichTextBox& obj)
8839{
8840 wxRichTextParagraphLayoutBox::Copy(obj);
8841}
8842
8843// Edit properties via a GUI
8844bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8845{
8846 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8847 boxDlg.SetAttributes(GetAttributes());
8848
8849 if (boxDlg.ShowModal() == wxID_OK)
8850 {
8851 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8852 // indeterminate in the object.
8853 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8854 return true;
5ad9ae3a 8855 }
603f702b
JS
8856 else
8857 return false;
bec80f4f
JS
8858}
8859
7c9fdebe
JS
8860/*!
8861 * wxRichTextField
8862 */
8863
8864IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8865
8866wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8867 wxRichTextParagraphLayoutBox(parent)
8868{
8869 SetFieldType(fieldType);
8870}
8871
8872/// Draw the item
8873bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8874{
8875 if (!IsShown())
8876 return true;
8877
8878 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8879 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8880 return true;
8881
8882 // Fallback; but don't draw guidelines.
8883 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8884 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8885}
8886
8887bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8888{
8889 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8890 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8891 return true;
8892
8893 // Fallback
8894 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8895}
8896
914a4e23 8897bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
7c9fdebe
JS
8898{
8899 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8900 if (fieldType)
914a4e23 8901 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
7c9fdebe 8902
914a4e23 8903 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
7c9fdebe
JS
8904}
8905
8906/// Calculate range
8907void wxRichTextField::CalculateRange(long start, long& end)
8908{
8909 if (IsTopLevel())
8910 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8911 else
8912 wxRichTextObject::CalculateRange(start, end);
8913}
8914
8915/// Copy
8916void wxRichTextField::Copy(const wxRichTextField& obj)
8917{
8918 wxRichTextParagraphLayoutBox::Copy(obj);
8919
32423dd8 8920 UpdateField(GetBuffer());
7c9fdebe
JS
8921}
8922
8923// Edit properties via a GUI
8924bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8925{
8926 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8927 if (fieldType)
8928 return fieldType->EditProperties(this, parent, buffer);
8929
8930 return false;
8931}
8932
8933bool wxRichTextField::CanEditProperties() const
8934{
8935 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8936 if (fieldType)
8937 return fieldType->CanEditProperties((wxRichTextField*) this);
8938
8939 return false;
8940}
8941
8942wxString wxRichTextField::GetPropertiesMenuLabel() const
8943{
8944 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8945 if (fieldType)
8946 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8947
8948 return wxEmptyString;
8949}
8950
32423dd8 8951bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
7c9fdebe
JS
8952{
8953 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8954 if (fieldType)
32423dd8 8955 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
7c9fdebe
JS
8956
8957 return false;
8958}
8959
8960bool wxRichTextField::IsTopLevel() const
8961{
8962 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8963 if (fieldType)
8964 return fieldType->IsTopLevel((wxRichTextField*) this);
8965
8966 return true;
8967}
8968
8969IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8970
8971IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8972
8973wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8974{
8975 Init();
8976
8977 SetName(name);
8978 SetLabel(label);
8979 SetDisplayStyle(displayStyle);
8980}
8981
8982wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8983{
8984 Init();
8985
8986 SetName(name);
8987 SetBitmap(bitmap);
8988 SetDisplayStyle(displayStyle);
8989}
8990
8991void wxRichTextFieldTypeStandard::Init()
8992{
8993 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8994 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8995 m_textColour = *wxWHITE;
8996 m_borderColour = *wxBLACK;
8997 m_backgroundColour = *wxBLACK;
8998 m_verticalPadding = 1;
8999 m_horizontalPadding = 3;
9000 m_horizontalMargin = 2;
9001 m_verticalMargin = 0;
9002}
9003
9004void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
9005{
9006 wxRichTextFieldType::Copy(field);
9007
9008 m_label = field.m_label;
9009 m_displayStyle = field.m_displayStyle;
9010 m_font = field.m_font;
9011 m_textColour = field.m_textColour;
9012 m_borderColour = field.m_borderColour;
9013 m_backgroundColour = field.m_backgroundColour;
9014 m_verticalPadding = field.m_verticalPadding;
9015 m_horizontalPadding = field.m_horizontalPadding;
9016 m_horizontalMargin = field.m_horizontalMargin;
9017 m_bitmap = field.m_bitmap;
9018}
9019
9020bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
9021{
9022 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9023 return false; // USe default composite drawing
9024 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9025 {
9026 int borderSize = 1;
9027
9028 wxPen borderPen(m_borderColour, 1, wxSOLID);
9029 wxBrush backgroundBrush(m_backgroundColour);
9030 wxColour textColour(m_textColour);
9031
9032 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9033 {
9034 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
9035 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
9036
9037 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
9038 backgroundBrush = wxBrush(highlightColour);
9039
9040 wxCheckSetBrush(dc, backgroundBrush);
9041 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9042 dc.DrawRectangle(rect);
9043 }
9044
9045 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9046 borderSize = 0;
9047
9048 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9049 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9050 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9051
9052 // clientArea is where the text is actually written
9053 wxRect clientArea = objectRect;
9054
9055 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9056 {
9057 dc.SetPen(borderPen);
9058 dc.SetBrush(backgroundBrush);
9059 dc.DrawRoundedRectangle(objectRect, 4.0);
9060 }
9061 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9062 {
9063 int arrowLength = objectRect.height/2;
9064 clientArea.width -= (arrowLength - m_horizontalPadding);
9065
9066 wxPoint pts[5];
9067 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9068 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9069 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9070 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9071 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9072 dc.SetPen(borderPen);
9073 dc.SetBrush(backgroundBrush);
9074 dc.DrawPolygon(5, pts);
9075 }
9076 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9077 {
9078 int arrowLength = objectRect.height/2;
9079 clientArea.width -= (arrowLength - m_horizontalPadding);
9080 clientArea.x += (arrowLength - m_horizontalPadding);
9081
9082 wxPoint pts[5];
9083 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9084 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9085 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9086 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9087 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9088 dc.SetPen(borderPen);
9089 dc.SetBrush(backgroundBrush);
9090 dc.DrawPolygon(5, pts);
9091 }
9092
9093 if (m_bitmap.IsOk())
9094 {
9095 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9096 int y = clientArea.y + m_verticalPadding;
9097 dc.DrawBitmap(m_bitmap, x, y, true);
9098
9099 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9100 {
9101 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9102 wxCheckSetPen(dc, *wxBLACK_PEN);
9103 dc.SetLogicalFunction(wxINVERT);
9104 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9105 dc.SetLogicalFunction(wxCOPY);
9106 }
9107 }
9108 else
9109 {
9110 wxString label(m_label);
9111 if (label.IsEmpty())
9112 label = wxT("??");
9113 int w, h, maxDescent;
9114 dc.SetFont(m_font);
9115 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9116 dc.SetTextForeground(textColour);
9117
9118 int x = clientArea.x + (clientArea.width - w)/2;
9119 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9120 dc.DrawText(m_label, x, y);
9121 }
9122 }
9123
9124 return true;
9125}
9126
9127bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9128{
9129 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9130 return false; // USe default composite layout
9131
9132 wxSize size = GetSize(obj, dc, context, style);
9133 obj->SetCachedSize(size);
9134 obj->SetMinSize(size);
9135 obj->SetMaxSize(size);
9136 return true;
9137}
9138
914a4e23 9139bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
7c9fdebe
JS
9140{
9141 if (IsTopLevel(obj))
914a4e23 9142 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
7c9fdebe
JS
9143 else
9144 {
9145 wxSize sz = GetSize(obj, dc, context, 0);
9146 if (partialExtents)
9147 {
9148 int lastSize;
9149 if (partialExtents->GetCount() > 0)
9150 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9151 else
9152 lastSize = 0;
9153 partialExtents->Add(lastSize + sz.x);
9154 }
9155 size = sz;
9156 return true;
9157 }
9158}
9159
9160wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9161{
9162 int borderSize = 1;
9163 int w = 0, h = 0, maxDescent = 0;
9164
9165 wxSize sz;
9166 if (m_bitmap.IsOk())
9167 {
9168 w = m_bitmap.GetWidth();
9169 h = m_bitmap.GetHeight();
9170
9171 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9172 }
9173 else
9174 {
9175 wxString label(m_label);
9176 if (label.IsEmpty())
9177 label = wxT("??");
9178 dc.SetFont(m_font);
9179 dc.GetTextExtent(label, & w, &h, & maxDescent);
9180
9181 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9182 }
9183
9184 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9185 {
9186 sz.x += borderSize*2;
9187 sz.y += borderSize*2;
9188 }
9189
9190 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9191 {
9192 // Add space for the arrow
9193 sz.x += (sz.y/2 - m_horizontalPadding);
9194 }
9195
9196 return sz;
9197}
9198
603f702b
JS
9199IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9200
9201wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9202 wxRichTextBox(parent)
bec80f4f 9203{
603f702b
JS
9204}
9205
9206/// Draw the item
8db2e3ef 9207bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9208{
8db2e3ef 9209 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
9210}
9211
9212/// Copy
9213void wxRichTextCell::Copy(const wxRichTextCell& obj)
9214{
9215 wxRichTextBox::Copy(obj);
9216}
9217
9218// Edit properties via a GUI
9219bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9220{
9221 // We need to gather common attributes for all selected cells.
9222
9223 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9224 bool multipleCells = false;
9225 wxRichTextAttr attr;
9226
9227 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9228 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 9229 {
603f702b
JS
9230 wxRichTextAttr clashingAttr, absentAttr;
9231 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9232 size_t i;
9233 int selectedCellCount = 0;
9234 for (i = 0; i < sel.GetCount(); i++)
9235 {
9236 const wxRichTextRange& range = sel[i];
9237 wxRichTextCell* cell = table->GetCell(range.GetStart());
9238 if (cell)
9239 {
9240 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 9241
603f702b
JS
9242 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9243
9244 selectedCellCount ++;
9245 }
9246 }
9247 multipleCells = selectedCellCount > 1;
5ad9ae3a 9248 }
603f702b
JS
9249 else
9250 {
9251 attr = GetAttributes();
9252 }
9253
9254 wxString caption;
9255 if (multipleCells)
9256 caption = _("Multiple Cell Properties");
9257 else
9258 caption = _("Cell Properties");
9259
9260 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9261 cellDlg.SetAttributes(attr);
9262
80a46597 9263 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
603f702b
JS
9264 if (sizePage)
9265 {
9266 // We don't want position and floating controls for a cell.
9267 sizePage->ShowPositionControls(false);
9268 sizePage->ShowFloatingControls(false);
9269 }
9270
9271 if (cellDlg.ShowModal() == wxID_OK)
9272 {
9273 if (multipleCells)
9274 {
9275 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9276 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9277 // since it may represent clashing attributes across multiple objects.
9278 table->SetCellStyle(sel, attr);
9279 }
9280 else
9281 // For a single object, indeterminate attributes set by the user should be reflected in the
9282 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9283 // the style directly instead of applying (which ignores indeterminate attributes,
9284 // leaving them as they were).
9285 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9286 return true;
9287 }
9288 else
9289 return false;
9290}
9291
9292WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9293
9294IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9295
9296wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9297{
9298 m_rowCount = 0;
9299 m_colCount = 0;
9300}
5ad9ae3a 9301
603f702b 9302// Draws the object.
8db2e3ef 9303bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 9304{
8db2e3ef 9305 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
9306}
9307
603f702b
JS
9308WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9309WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9310
9311// Lays the object out. rect is the space available for layout. Often it will
9312// be the specified overall space for this object, if trying to constrain
9313// layout to a particular size, or it could be the total space available in the
9314// parent. rect is the overall size, so we must subtract margins and padding.
9315// to get the actual available space.
8db2e3ef 9316bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 9317{
603f702b
JS
9318 SetPosition(rect.GetPosition());
9319
9320 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9321 // minimum size if within alloted size, then divide up remaining size
9322 // between rows/cols.
9323
9324 double scale = 1.0;
9325 wxRichTextBuffer* buffer = GetBuffer();
9326 if (buffer) scale = buffer->GetScale();
9327
8db2e3ef 9328 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
9329 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9330
8db2e3ef
JS
9331 wxRichTextAttr attr(GetAttributes());
9332 context.ApplyVirtualAttributes(attr, this);
9333
603f702b
JS
9334 // If we have no fixed table size, and assuming we're not pushed for
9335 // space, then we don't have to try to stretch the table to fit the contents.
9336 bool stretchToFitTableWidth = false;
9337
9338 int tableWidth = rect.width;
8db2e3ef 9339 if (attr.GetTextBoxAttr().GetWidth().IsValid())
603f702b 9340 {
8db2e3ef 9341 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
9342
9343 // Fixed table width, so we do want to stretch columns out if necessary.
9344 stretchToFitTableWidth = true;
9345
9346 // Shouldn't be able to exceed the size passed to this function
9347 tableWidth = wxMin(rect.width, tableWidth);
9348 }
9349
9350 // Get internal padding
36307fdf 9351 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
9352 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9353 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9354 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9355 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
9356
9357 // Assume that left and top padding are also used for inter-cell padding.
9358 int paddingX = paddingLeft;
9359 int paddingY = paddingTop;
9360
9361 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 9362 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
9363
9364 // Internal table width - the area for content
9365 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9366
9367 int rowCount = m_cells.GetCount();
9368 if (m_colCount == 0 || rowCount == 0)
9369 {
9370 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9371 SetCachedSize(overallRect.GetSize());
9372
9373 // Zero content size
9374 SetMinSize(overallRect.GetSize());
9375 SetMaxSize(GetMinSize());
9376 return true;
9377 }
9378
9379 // The final calculated widths
bb7bbd12
JS
9380 wxArrayInt colWidths;
9381 colWidths.Add(0, m_colCount);
603f702b 9382
bb7bbd12
JS
9383 wxArrayInt absoluteColWidths;
9384 absoluteColWidths.Add(0, m_colCount);
7c9fdebe 9385
bb7bbd12
JS
9386 wxArrayInt percentageColWidths;
9387 percentageColWidths.Add(0, m_colCount);
603f702b
JS
9388 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9389 // These are only relevant when the first column contains spanning information.
9390 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
9391 wxArrayInt maxColWidths;
9392 maxColWidths.Add(0, m_colCount);
9393 wxArrayInt minColWidths;
9394 minColWidths.Add(0, m_colCount);
603f702b
JS
9395
9396 wxSize tableSize(tableWidth, 0);
9397
9398 int i, j, k;
9399
9400 for (i = 0; i < m_colCount; i++)
9401 {
9402 absoluteColWidths[i] = 0;
9403 // absoluteColWidthsSpanning[i] = 0;
9404 percentageColWidths[i] = -1;
9405 // percentageColWidthsSpanning[i] = -1;
9406 colWidths[i] = 0;
9407 maxColWidths[i] = 0;
9408 minColWidths[i] = 0;
9409 // columnSpans[i] = 1;
9410 }
9411
9412 // (0) Determine which cells are visible according to spans
9413 // 1 2 3 4 5
9414 // __________________
9415 // | | | | | 1
9416 // |------| |----|
9417 // |------| | | 2
9418 // |------| | | 3
9419 // |------------------|
9420 // |__________________| 4
9421
9422 // To calculate cell visibility:
9423 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9424 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9425 // that cell, hide the cell.
9426
9427 // We can also use this array to match the size of spanning cells to the grid. Or just do
9428 // this when we iterate through all cells.
9429
9430 // 0.1: add spanning cells to an array
9431 wxRichTextRectArray rectArray;
9432 for (j = 0; j < m_rowCount; j++)
9433 {
9434 for (i = 0; i < m_colCount; i++)
9435 {
9436 wxRichTextBox* cell = GetCell(j, i);
9437 int colSpan = 1, rowSpan = 1;
9438 if (cell->GetProperties().HasProperty(wxT("colspan")))
9439 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9440 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9441 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9442 if (colSpan > 1 || rowSpan > 1)
9443 {
9444 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9445 }
9446 }
9447 }
9448 // 0.2: find which cells are subsumed by a spanning cell
9449 for (j = 0; j < m_rowCount; j++)
9450 {
9451 for (i = 0; i < m_colCount; i++)
9452 {
9453 wxRichTextBox* cell = GetCell(j, i);
9454 if (rectArray.GetCount() == 0)
9455 {
9456 cell->Show(true);
9457 }
9458 else
9459 {
9460 int colSpan = 1, rowSpan = 1;
9461 if (cell->GetProperties().HasProperty(wxT("colspan")))
9462 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9463 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9464 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9465 if (colSpan > 1 || rowSpan > 1)
9466 {
9467 // Assume all spanning cells are shown
9468 cell->Show(true);
9469 }
9470 else
9471 {
9472 bool shown = true;
9473 for (k = 0; k < (int) rectArray.GetCount(); k++)
9474 {
9475 if (rectArray[k].Contains(wxPoint(i, j)))
9476 {
9477 shown = false;
9478 break;
9479 }
9480 }
9481 cell->Show(shown);
9482 }
9483 }
9484 }
9485 }
9486
9487 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9488 // overlap with a spanned cell starting at a previous column position.
9489 // This means we need to keep an array of rects so we can check. However
9490 // it does also mean that some spans simply may not be taken into account
9491 // where there are different spans happening on different rows. In these cases,
9492 // they will simply be as wide as their constituent columns.
9493
9494 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9495 // the absolute or percentage width of each column.
9496
9497 for (j = 0; j < m_rowCount; j++)
9498 {
9499 // First get the overall margins so we can calculate percentage widths based on
9500 // the available content space for all cells on the row
9501
9502 int overallRowContentMargin = 0;
9503 int visibleCellCount = 0;
9504
9505 for (i = 0; i < m_colCount; i++)
9506 {
9507 wxRichTextBox* cell = GetCell(j, i);
9508 if (cell->IsShown())
9509 {
9510 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9511 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9512
9513 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9514 visibleCellCount ++;
9515 }
9516 }
9517
9518 // Add in inter-cell padding
9519 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9520
9521 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9522 wxSize rowTableSize(rowContentWidth, 0);
9523 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9524
9525 for (i = 0; i < m_colCount; i++)
9526 {
9527 wxRichTextBox* cell = GetCell(j, i);
9528 if (cell->IsShown())
9529 {
9530 int colSpan = 1;
9531 if (cell->GetProperties().HasProperty(wxT("colspan")))
9532 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9533
9534 // Lay out cell to find min/max widths
9535 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9536 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
9537
9538 if (colSpan == 1)
9539 {
9540 int absoluteCellWidth = -1;
9541 int percentageCellWidth = -1;
9542
9543 // I think we need to calculate percentages from the internal table size,
9544 // minus the padding between cells which we'll need to calculate from the
9545 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9546 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9547 // so if we want to conform to that we'll need to add in the overall cell margins.
9548 // However, this will make it difficult to specify percentages that add up to
9549 // 100% and still fit within the table width.
9550 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9551 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9552 // If we're using internal content size for the width, we would calculate the
9553 // the overall cell width for n cells as:
9554 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9555 // + thisOverallCellMargin
9556 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9557 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9558
9559 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9560 {
9561 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9562 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9563 {
9564 percentageCellWidth = w;
9565 }
9566 else
9567 {
9568 absoluteCellWidth = w;
9569 }
9570 // Override absolute width with minimum width if necessary
9571 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9572 absoluteCellWidth = cell->GetMinSize().x;
9573 }
9574
9575 if (absoluteCellWidth != -1)
9576 {
9577 if (absoluteCellWidth > absoluteColWidths[i])
9578 absoluteColWidths[i] = absoluteCellWidth;
9579 }
9580
9581 if (percentageCellWidth != -1)
9582 {
9583 if (percentageCellWidth > percentageColWidths[i])
9584 percentageColWidths[i] = percentageCellWidth;
9585 }
9586
9587 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9588 minColWidths[i] = cell->GetMinSize().x;
9589 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9590 maxColWidths[i] = cell->GetMaxSize().x;
9591 }
9592 }
9593 }
9594 }
9595
9596 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9597 // TODO: simply merge this into (1).
9598 for (i = 0; i < m_colCount; i++)
9599 {
9600 if (absoluteColWidths[i] > 0)
9601 {
9602 colWidths[i] = absoluteColWidths[i];
9603 }
9604 else if (percentageColWidths[i] > 0)
9605 {
9606 colWidths[i] = percentageColWidths[i];
9607
9608 // This is rubbish - we calculated the absolute widths from percentages, so
9609 // we can't do it again here.
9610 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9611 }
9612 }
9613
9614 // (3) Process absolute or proportional widths of spanning columns,
9615 // now that we know what our fixed column widths are going to be.
9616 // Spanned cells will try to adjust columns so the span will fit.
9617 // Even existing fixed column widths can be expanded if necessary.
9618 // Actually, currently fixed columns widths aren't adjusted; instead,
9619 // the algorithm favours earlier rows and adjusts unspecified column widths
9620 // the first time only. After that, we can't know whether the column has been
9621 // specified explicitly or not. (We could make a note if necessary.)
9622 for (j = 0; j < m_rowCount; j++)
9623 {
9624 // First get the overall margins so we can calculate percentage widths based on
9625 // the available content space for all cells on the row
9626
9627 int overallRowContentMargin = 0;
9628 int visibleCellCount = 0;
9629
9630 for (i = 0; i < m_colCount; i++)
9631 {
9632 wxRichTextBox* cell = GetCell(j, i);
9633 if (cell->IsShown())
9634 {
9635 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9636 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9637
9638 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9639 visibleCellCount ++;
9640 }
9641 }
9642
9643 // Add in inter-cell padding
9644 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9645
9646 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9647 wxSize rowTableSize(rowContentWidth, 0);
9648 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9649
9650 for (i = 0; i < m_colCount; i++)
9651 {
9652 wxRichTextBox* cell = GetCell(j, i);
9653 if (cell->IsShown())
9654 {
9655 int colSpan = 1;
9656 if (cell->GetProperties().HasProperty(wxT("colspan")))
9657 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9658
9659 if (colSpan > 1)
9660 {
9661 int spans = wxMin(colSpan, m_colCount - i);
9662 int cellWidth = 0;
9663 if (spans > 0)
9664 {
9665 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9666 {
9667 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9668 // Override absolute width with minimum width if necessary
9669 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9670 cellWidth = cell->GetMinSize().x;
9671 }
9672 else
9673 {
9674 // Do we want to do this? It's the only chance we get to
9675 // use the cell's min/max sizes, so we need to work out
9676 // how we're going to balance the unspecified spanning cell
9677 // width with the possibility more-constrained constituent cell widths.
9678 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9679 // don't want to constraint all the spanned columns to fit into this cell.
9680 // OK, let's say that if any of the constituent columns don't fit,
9681 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9682 // cells to the columns later.
9683 cellWidth = cell->GetMinSize().x;
9684 if (cell->GetMaxSize().x > cellWidth)
9685 cellWidth = cell->GetMaxSize().x;
9686 }
9687
9688 // Subtract the padding between cells
9689 int spanningWidth = cellWidth;
9690 spanningWidth -= paddingX * (spans-1);
9691
9692 if (spanningWidth > 0)
9693 {
9694 // Now share the spanning width between columns within that span
9695 // TODO: take into account min widths of columns within the span
9696 int spanningWidthLeft = spanningWidth;
9697 int stretchColCount = 0;
9698 for (k = i; k < (i+spans); k++)
9699 {
9700 if (colWidths[k] > 0) // absolute or proportional width has been specified
9701 spanningWidthLeft -= colWidths[k];
9702 else
9703 stretchColCount ++;
9704 }
9705 // Now divide what's left between the remaining columns
9706 int colShare = 0;
9707 if (stretchColCount > 0)
9708 colShare = spanningWidthLeft / stretchColCount;
9709 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9710
9711 // If fixed-width columns are currently too big, then we'll later
9712 // stretch the spanned cell to fit.
9713
9714 if (spanningWidthLeft > 0)
9715 {
9716 for (k = i; k < (i+spans); k++)
9717 {
9718 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9719 {
9720 int newWidth = colShare;
9721 if (k == (i+spans-1))
9722 newWidth += colShareRemainder; // ensure all pixels are filled
9723 colWidths[k] = newWidth;
9724 }
9725 }
9726 }
9727 }
9728 }
9729 }
9730 }
9731 }
9732 }
9733
9734 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9735 // TODO: take into account min widths of columns within the span
9736 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9737 int widthLeft = tableWidthMinusPadding;
9738 int stretchColCount = 0;
9739 for (i = 0; i < m_colCount; i++)
9740 {
9741 // TODO: we need to take into account min widths.
9742 // Subtract min width from width left, then
9743 // add the colShare to the min width
9744 if (colWidths[i] > 0) // absolute or proportional width has been specified
9745 widthLeft -= colWidths[i];
9746 else
9747 {
9748 if (minColWidths[i] > 0)
9749 widthLeft -= minColWidths[i];
9750
9751 stretchColCount ++;
9752 }
9753 }
9754
9755 // Now divide what's left between the remaining columns
9756 int colShare = 0;
9757 if (stretchColCount > 0)
9758 colShare = widthLeft / stretchColCount;
9759 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9760
9761 // Check we don't have enough space, in which case shrink all columns, overriding
9762 // any absolute/proportional widths
9763 // TODO: actually we would like to divide up the shrinkage according to size.
9764 // How do we calculate the proportions that will achieve this?
9765 // Could first choose an arbitrary value for stretching cells, and then calculate
9766 // factors to multiply each width by.
9767 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9768 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9769 {
9770 colShare = tableWidthMinusPadding / m_colCount;
9771 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9772 for (i = 0; i < m_colCount; i++)
9773 {
9774 colWidths[i] = 0;
9775 minColWidths[i] = 0;
9776 }
9777 }
9778
9779 // We have to adjust the columns if either we need to shrink the
9780 // table to fit the parent/table width, or we explicitly set the
9781 // table width and need to stretch out the table.
9782 if (widthLeft < 0 || stretchToFitTableWidth)
9783 {
9784 for (i = 0; i < m_colCount; i++)
9785 {
9786 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9787 {
9788 if (minColWidths[i] > 0)
9789 colWidths[i] = minColWidths[i] + colShare;
9790 else
9791 colWidths[i] = colShare;
9792 if (i == (m_colCount-1))
9793 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9794 }
9795 }
9796 }
9797
9798 // TODO: if spanned cells have no specified or max width, make them the
9799 // as big as the columns they span. Do this for all spanned cells in all
9800 // rows, of course. Size any spanned cells left over at the end - even if they
9801 // have width > 0, make sure they're limited to the appropriate column edge.
9802
9803
9804/*
9805 Sort out confusion between content width
9806 and overall width later. For now, assume we specify overall width.
9807
9808 So, now we've laid out the table to fit into the given space
9809 and have used specified widths and minimum widths.
9810
9811 Now we need to consider how we will try to take maximum width into account.
9812
9813*/
9814
9815 // (??) TODO: take max width into account
9816
9817 // (6) Lay out all cells again with the current values
9818
9819 int maxRight = 0;
9820 int y = availableSpace.y;
9821 for (j = 0; j < m_rowCount; j++)
9822 {
9823 int x = availableSpace.x; // TODO: take into account centering etc.
9824 int maxCellHeight = 0;
9825 int maxSpecifiedCellHeight = 0;
9826
bb7bbd12
JS
9827 wxArrayInt actualWidths;
9828 actualWidths.Add(0, m_colCount);
603f702b
JS
9829
9830 wxTextAttrDimensionConverter converter(dc, scale);
9831 for (i = 0; i < m_colCount; i++)
9832 {
9833 wxRichTextCell* cell = GetCell(j, i);
9834 if (cell->IsShown())
9835 {
603f702b
JS
9836 // Get max specified cell height
9837 // Don't handle percentages for height
9838 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9839 {
9840 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9841 if (h > maxSpecifiedCellHeight)
9842 maxSpecifiedCellHeight = h;
9843 }
9844
9845 if (colWidths[i] > 0) // absolute or proportional width has been specified
9846 {
9847 int colSpan = 1;
9848 if (cell->GetProperties().HasProperty(wxT("colspan")))
9849 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9850
9851 wxRect availableCellSpace;
9852
9853 // TODO: take into acount spans
9854 if (colSpan > 1)
9855 {
9856 // Calculate the size of this spanning cell from its constituent columns
9857 int xx = x;
9858 int spans = wxMin(colSpan, m_colCount - i);
9859 for (k = i; k < spans; k++)
9860 {
9861 if (k != i)
9862 xx += paddingX;
9863 xx += colWidths[k];
9864 }
9865 availableCellSpace = wxRect(x, y, xx, -1);
9866 }
9867 else
9868 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9869
9870 // Store actual width so we can force cell to be the appropriate width on the final loop
9871 actualWidths[i] = availableCellSpace.GetWidth();
9872
9873 // Lay out cell
9874 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9875 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9876
9877 // TODO: use GetCachedSize().x to compute 'natural' size
9878
9879 x += (availableCellSpace.GetWidth() + paddingX);
9880 if (cell->GetCachedSize().y > maxCellHeight)
9881 maxCellHeight = cell->GetCachedSize().y;
9882 }
9883 }
9884 }
9885
9886 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9887
9888 for (i = 0; i < m_colCount; i++)
9889 {
9890 wxRichTextCell* cell = GetCell(j, i);
9891 if (cell->IsShown())
9892 {
9893 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9894 // Lay out cell with new height
9895 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9896 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9897
9898 // Make sure the cell size really is the appropriate size,
9899 // not the calculated box size
9900 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9901
9902 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9903 }
9904 }
9905
9906 y += maxCellHeight;
9907 if (j < (m_rowCount-1))
9908 y += paddingY;
9909 }
9910
9911 // We need to add back the margins etc.
9912 {
9913 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9914 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 9915 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
9916 SetCachedSize(marginRect.GetSize());
9917 }
9918
9919 // TODO: calculate max size
9920 {
9921 SetMaxSize(GetCachedSize());
9922 }
9923
9924 // TODO: calculate min size
9925 {
9926 SetMinSize(GetCachedSize());
9927 }
9928
9929 // TODO: currently we use either a fixed table width or the parent's size.
9930 // We also want to be able to calculate the table width from its content,
9931 // whether using fixed column widths or cell content min/max width.
9932 // Probably need a boolean flag to say whether we need to stretch cells
9933 // to fit the table width, or to simply use min/max cell widths. The
9934 // trouble with this is that if cell widths are not specified, they
9935 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9936 // Anyway, ignoring that problem, we probably need to factor layout into a function
9937 // that can can calculate the maximum unconstrained layout in case table size is
9938 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9939 // constrain Layout(), or the previously-calculated max size to constraint layout.
9940
9941 return true;
9942}
9943
9944// Finds the absolute position and row height for the given character position
8db2e3ef 9945bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
9946{
9947 wxRichTextCell* child = GetCell(index+1);
9948 if (child)
9949 {
9950 // Find the position at the start of the child cell, since the table doesn't
9951 // have any caret position of its own.
8db2e3ef 9952 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
9953 }
9954 else
9955 return false;
9956}
9957
9958// Get the cell at the given character position (in the range of the table).
9959wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9960{
9961 int row = 0, col = 0;
9962 if (GetCellRowColumnPosition(pos, row, col))
9963 {
9964 return GetCell(row, col);
9965 }
9966 else
9967 return NULL;
9968}
9969
9970// Get the row/column for a given character position
9971bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9972{
9973 if (m_colCount == 0 || m_rowCount == 0)
9974 return false;
9975
9976 row = (int) (pos / m_colCount);
9977 col = pos - (row * m_colCount);
9978
9979 wxASSERT(row < m_rowCount && col < m_colCount);
9980
9981 if (row < m_rowCount && col < m_colCount)
9982 return true;
9983 else
9984 return false;
9985}
9986
9987// Calculate range, taking row/cell ordering into account instead of relying
9988// on list ordering.
9989void wxRichTextTable::CalculateRange(long start, long& end)
9990{
9991 long current = start;
9992 long lastEnd = current;
9993
9994 if (IsTopLevel())
9995 {
9996 current = 0;
9997 lastEnd = 0;
9998 }
9999
10000 int i, j;
10001 for (i = 0; i < m_rowCount; i++)
10002 {
10003 for (j = 0; j < m_colCount; j++)
10004 {
10005 wxRichTextCell* child = GetCell(i, j);
10006 if (child)
10007 {
10008 long childEnd = 0;
10009
10010 child->CalculateRange(current, childEnd);
10011
10012 lastEnd = childEnd;
10013 current = childEnd + 1;
10014 }
10015 }
10016 }
10017
10018 // A top-level object always has a range of size 1,
10019 // because its children don't count at this level.
10020 end = start;
10021 m_range.SetRange(start, start);
10022
10023 // An object with no children has zero length
10024 if (m_children.GetCount() == 0)
10025 lastEnd --;
10026 m_ownRange.SetRange(0, lastEnd);
10027}
10028
10029// Gets the range size.
914a4e23 10030bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
603f702b 10031{
914a4e23 10032 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
603f702b
JS
10033}
10034
10035// Deletes content in the given range.
10036bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
10037{
10038 // TODO: implement deletion of cells
10039 return true;
10040}
10041
10042// Gets any text in this object for the given range.
10043wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10044{
10045 return wxRichTextBox::GetTextForRange(range);
10046}
10047
10048// Copies this object.
10049void wxRichTextTable::Copy(const wxRichTextTable& obj)
10050{
10051 wxRichTextBox::Copy(obj);
10052
10053 ClearTable();
10054
10055 m_rowCount = obj.m_rowCount;
10056 m_colCount = obj.m_colCount;
10057
10058 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10059
10060 int i, j;
10061 for (i = 0; i < m_rowCount; i++)
10062 {
10063 wxRichTextObjectPtrArray& colArray = m_cells[i];
10064 for (j = 0; j < m_colCount; j++)
10065 {
10066 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10067 AppendChild(cell);
10068
10069 colArray.Add(cell);
10070 }
10071 }
10072}
10073
10074void wxRichTextTable::ClearTable()
10075{
10076 m_cells.Clear();
10077 DeleteChildren();
10078}
10079
10080bool wxRichTextTable::CreateTable(int rows, int cols)
10081{
10082 ClearTable();
10083
10084 m_rowCount = rows;
10085 m_colCount = cols;
10086
10087 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10088
10089 int i, j;
10090 for (i = 0; i < rows; i++)
10091 {
10092 wxRichTextObjectPtrArray& colArray = m_cells[i];
10093 for (j = 0; j < cols; j++)
10094 {
10095 wxRichTextCell* cell = new wxRichTextCell;
10096 AppendChild(cell);
10097 cell->AddParagraph(wxEmptyString);
10098
10099 colArray.Add(cell);
10100 }
10101 }
10102
10103 return true;
10104}
10105
10106wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10107{
10108 wxASSERT(row < m_rowCount);
10109 wxASSERT(col < m_colCount);
10110
10111 if (row < m_rowCount && col < m_colCount)
10112 {
10113 wxRichTextObjectPtrArray& colArray = m_cells[row];
10114 wxRichTextObject* obj = colArray[col];
10115 return wxDynamicCast(obj, wxRichTextCell);
10116 }
10117 else
d67faa04 10118 return NULL;
603f702b
JS
10119}
10120
10121// Returns a selection object specifying the selections between start and end character positions.
10122// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10123wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10124{
10125 wxRichTextSelection selection;
10126 selection.SetContainer((wxRichTextTable*) this);
10127
10128 if (start > end)
10129 {
10130 long tmp = end;
10131 end = start;
10132 start = tmp;
10133 }
10134
10135 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10136
10137 if (end >= (m_colCount * m_rowCount))
10138 return selection;
10139
10140 // We need to find the rectangle of cells that is described by the rectangle
10141 // with start, end as the diagonal. Make sure we don't add cells that are
10142 // not currenty visible because they are overlapped by spanning cells.
10143/*
10144 --------------------------
10145 | 0 | 1 | 2 | 3 | 4 |
10146 --------------------------
10147 | 5 | 6 | 7 | 8 | 9 |
10148 --------------------------
10149 | 10 | 11 | 12 | 13 | 14 |
10150 --------------------------
10151 | 15 | 16 | 17 | 18 | 19 |
10152 --------------------------
10153
10154 Let's say we select 6 -> 18.
10155
10156 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10157 which is left and which is right.
10158
10159 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10160
10161 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10162 and (b) shown.
10163
10164
10165*/
10166
10167 int leftCol = start - m_colCount * int(start/m_colCount);
10168 int rightCol = end - m_colCount * int(end/m_colCount);
10169
10170 int topRow = int(start/m_colCount);
10171 int bottomRow = int(end/m_colCount);
10172
10173 if (leftCol > rightCol)
10174 {
10175 int tmp = rightCol;
10176 rightCol = leftCol;
10177 leftCol = tmp;
10178 }
10179
10180 if (topRow > bottomRow)
10181 {
10182 int tmp = bottomRow;
10183 bottomRow = topRow;
10184 topRow = tmp;
10185 }
10186
10187 int i, j;
10188 for (i = topRow; i <= bottomRow; i++)
10189 {
10190 for (j = leftCol; j <= rightCol; j++)
10191 {
10192 wxRichTextCell* cell = GetCell(i, j);
10193 if (cell && cell->IsShown())
10194 selection.Add(cell->GetRange());
10195 }
10196 }
10197
10198 return selection;
10199}
10200
10201// Sets the attributes for the cells specified by the selection.
10202bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10203{
10204 if (selection.GetContainer() != this)
10205 return false;
10206
10207 wxRichTextBuffer* buffer = GetBuffer();
10208 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10209 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10210
10211 if (withUndo)
10212 buffer->BeginBatchUndo(_("Set Cell Style"));
10213
10214 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10215 while (node)
10216 {
10217 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10218 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10219 SetStyle(cell, style, flags);
10220 node = node->GetNext();
10221 }
10222
10223 // Do action, or delay it until end of batch.
10224 if (withUndo)
10225 buffer->EndBatchUndo();
10226
10227 return true;
10228}
10229
10230bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10231{
10232 wxASSERT((startRow + noRows) < m_rowCount);
10233 if ((startRow + noRows) >= m_rowCount)
10234 return false;
10235
10236 int i, j;
10237 for (i = startRow; i < (startRow+noRows); i++)
10238 {
10239 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10240 for (j = 0; j < (int) colArray.GetCount(); j++)
10241 {
10242 wxRichTextObject* cell = colArray[j];
10243 RemoveChild(cell, true);
10244 }
10245
10246 // Keep deleting at the same position, since we move all
10247 // the others up
10248 m_cells.RemoveAt(startRow);
10249 }
10250
10251 m_rowCount = m_rowCount - noRows;
10252
10253 return true;
10254}
10255
10256bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10257{
10258 wxASSERT((startCol + noCols) < m_colCount);
10259 if ((startCol + noCols) >= m_colCount)
10260 return false;
10261
10262 bool deleteRows = (noCols == m_colCount);
10263
10264 int i, j;
10265 for (i = 0; i < m_rowCount; i++)
10266 {
10267 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
10268 for (j = startCol; j < (startCol+noCols); j++)
10269 {
10270 wxRichTextObject* cell = colArray[j];
10271 RemoveChild(cell, true);
10272 }
10273
10274 if (deleteRows)
10275 m_cells.RemoveAt(0);
10276 }
10277
10278 if (deleteRows)
10279 m_rowCount = 0;
10280 m_colCount = m_colCount - noCols;
10281
10282 return true;
10283}
10284
10285bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10286{
10287 wxASSERT(startRow <= m_rowCount);
10288 if (startRow > m_rowCount)
10289 return false;
10290
10291 int i, j;
10292 for (i = 0; i < noRows; i++)
10293 {
10294 int idx;
10295 if (startRow == m_rowCount)
10296 {
10297 m_cells.Add(wxRichTextObjectPtrArray());
10298 idx = m_cells.GetCount() - 1;
10299 }
10300 else
10301 {
10302 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10303 idx = startRow+i;
10304 }
10305
10306 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10307 for (j = 0; j < m_colCount; j++)
10308 {
10309 wxRichTextCell* cell = new wxRichTextCell;
10310 cell->GetAttributes() = attr;
10311
10312 AppendChild(cell);
10313 colArray.Add(cell);
10314 }
10315 }
10316
10317 m_rowCount = m_rowCount + noRows;
10318 return true;
10319}
10320
10321bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10322{
10323 wxASSERT(startCol <= m_colCount);
10324 if (startCol > m_colCount)
10325 return false;
10326
10327 int i, j;
10328 for (i = 0; i < m_rowCount; i++)
10329 {
10330 wxRichTextObjectPtrArray& colArray = m_cells[i];
10331 for (j = 0; j < noCols; j++)
10332 {
10333 wxRichTextCell* cell = new wxRichTextCell;
10334 cell->GetAttributes() = attr;
10335
10336 AppendChild(cell);
10337
10338 if (startCol == m_colCount)
10339 colArray.Add(cell);
10340 else
10341 colArray.Insert(cell, startCol+j);
10342 }
10343 }
10344
10345 m_colCount = m_colCount + noCols;
10346
10347 return true;
5ad9ae3a
JS
10348}
10349
603f702b
JS
10350// Edit properties via a GUI
10351bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 10352{
603f702b
JS
10353 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10354 boxDlg.SetAttributes(GetAttributes());
10355
10356 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 10357 {
603f702b
JS
10358 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10359 return true;
5ad9ae3a
JS
10360 }
10361 else
10362 return false;
bec80f4f
JS
10363}
10364
5d7836c4
JS
10365/*
10366 * Module to initialise and clean up handlers
10367 */
10368
10369class wxRichTextModule: public wxModule
10370{
10371DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10372public:
10373 wxRichTextModule() {}
cfa3b256
JS
10374 bool OnInit()
10375 {
d2d0adc7 10376 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
10377 wxRichTextBuffer::InitStandardHandlers();
10378 wxRichTextParagraph::InitDefaultTabs();
1aca9fcd
JS
10379
10380 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10381 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10382 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10385 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10386 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10387 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10388 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10389
cfa3b256 10390 return true;
47b378bd 10391 }
cfa3b256
JS
10392 void OnExit()
10393 {
10394 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 10395 wxRichTextBuffer::CleanUpDrawingHandlers();
7c9fdebe 10396 wxRichTextBuffer::CleanUpFieldTypes();
1aca9fcd 10397 wxRichTextXMLHandler::ClearNodeToClassMap();
cfa3b256
JS
10398 wxRichTextDecimalToRoman(-1);
10399 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 10400 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 10401 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 10402 }
5d7836c4
JS
10403};
10404
10405IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10406
10407
f1d6804f
RD
10408// If the richtext lib is dynamically loaded after the app has already started
10409// (such as from wxPython) then the built-in module system will not init this
10410// module. Provide this function to do it manually.
10411void wxRichTextModuleInit()
10412{
10413 wxModule* module = new wxRichTextModule;
10414 module->Init();
10415 wxModule::RegisterModule(module);
10416}
10417
10418
5d7836c4
JS
10419/*!
10420 * Commands for undo/redo
10421 *
10422 */
10423
10424wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 10425 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 10426{
603f702b 10427 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
10428}
10429
7fe8059f 10430wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
10431{
10432}
10433
10434wxRichTextCommand::~wxRichTextCommand()
10435{
10436 ClearActions();
10437}
10438
10439void wxRichTextCommand::AddAction(wxRichTextAction* action)
10440{
10441 if (!m_actions.Member(action))
10442 m_actions.Append(action);
10443}
10444
10445bool wxRichTextCommand::Do()
10446{
09f14108 10447 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
10448 {
10449 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10450 action->Do();
10451 }
10452
10453 return true;
10454}
10455
10456bool wxRichTextCommand::Undo()
10457{
09f14108 10458 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
10459 {
10460 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10461 action->Undo();
10462 }
10463
10464 return true;
10465}
10466
10467void wxRichTextCommand::ClearActions()
10468{
10469 WX_CLEAR_LIST(wxList, m_actions);
10470}
10471
10472/*!
10473 * Individual action
10474 *
10475 */
10476
603f702b
JS
10477wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10478 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10479 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
10480{
10481 m_buffer = buffer;
603f702b
JS
10482 m_object = NULL;
10483 m_containerAddress.Create(buffer, container);
5d7836c4
JS
10484 m_ignoreThis = ignoreFirstTime;
10485 m_cmdId = id;
10486 m_position = -1;
10487 m_ctrl = ctrl;
10488 m_name = name;
10489 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10490 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10491 if (cmd)
10492 cmd->AddAction(this);
10493}
10494
10495wxRichTextAction::~wxRichTextAction()
10496{
603f702b
JS
10497 if (m_object)
10498 delete m_object;
10499}
10500
10501// Returns the container that this action refers to, using the container address and top-level buffer.
10502wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10503{
10504 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10505 return container;
5d7836c4
JS
10506}
10507
603f702b 10508
7051fa41
JS
10509void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10510{
10511 // Store a list of line start character and y positions so we can figure out which area
10512 // we need to refresh
10513
10514#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
10515 wxRichTextParagraphLayoutBox* container = GetContainer();
10516 wxASSERT(container != NULL);
10517 if (!container)
10518 return;
10519
7051fa41
JS
10520 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10521 // If we had several actions, which only invalidate and leave layout until the
10522 // paint handler is called, then this might not be true. So we may need to switch
10523 // optimisation on only when we're simply adding text and not simultaneously
10524 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10525 // first, but of course this means we'll be doing it twice.
603f702b 10526 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41 10527 {
4ba36292
JS
10528 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10529 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
7051fa41
JS
10530 int lastY = firstVisiblePt.y + clientSize.y;
10531
603f702b
JS
10532 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10533 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
10534 while (node)
10535 {
10536 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10537 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10538 while (node2)
10539 {
10540 wxRichTextLine* line = node2->GetData();
10541 wxPoint pt = line->GetAbsolutePosition();
10542 wxRichTextRange range = line->GetAbsoluteRange();
10543
10544 if (pt.y > lastY)
10545 {
10546 node2 = wxRichTextLineList::compatibility_iterator();
10547 node = wxRichTextObjectList::compatibility_iterator();
10548 }
10549 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10550 {
10551 optimizationLineCharPositions.Add(range.GetStart());
10552 optimizationLineYPositions.Add(pt.y);
10553 }
10554
10555 if (node2)
10556 node2 = node2->GetNext();
10557 }
10558
10559 if (node)
10560 node = node->GetNext();
10561 }
10562 }
10563#endif
10564}
10565
5d7836c4
JS
10566bool wxRichTextAction::Do()
10567{
10568 m_buffer->Modify(true);
10569
603f702b
JS
10570 wxRichTextParagraphLayoutBox* container = GetContainer();
10571 wxASSERT(container != NULL);
10572 if (!container)
10573 return false;
10574
5d7836c4
JS
10575 switch (m_cmdId)
10576 {
10577 case wxRICHTEXT_INSERT:
10578 {
ea160b2e
JS
10579 // Store a list of line start character and y positions so we can figure out which area
10580 // we need to refresh
10581 wxArrayInt optimizationLineCharPositions;
10582 wxArrayInt optimizationLineYPositions;
10583
10584#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 10585 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
10586#endif
10587
603f702b
JS
10588 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10589 container->UpdateRanges();
10590
10591 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10592 // Layout() would stop prematurely at the top level.
10593 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 10594
603f702b 10595 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
10596
10597 // Character position to caret position
10598 newCaretPosition --;
10599
10600 // Don't take into account the last newline
5d7836c4
JS
10601 if (m_newParagraphs.GetPartialParagraph())
10602 newCaretPosition --;
46ee0e5b 10603 else
7c081bd2 10604 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
10605 {
10606 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10607 if (p->GetRange().GetLength() == 1)
10608 newCaretPosition --;
10609 }
5d7836c4 10610
603f702b 10611 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 10612
7051fa41 10613 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 10614
5912d19e
JS
10615 wxRichTextEvent cmdEvent(
10616 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10617 m_ctrl ? m_ctrl->GetId() : -1);
10618 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10619 cmdEvent.SetRange(GetRange());
10620 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10621 cmdEvent.SetContainer(container);
3e541562 10622
5912d19e 10623 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
10624
10625 break;
10626 }
10627 case wxRICHTEXT_DELETE:
10628 {
7051fa41
JS
10629 wxArrayInt optimizationLineCharPositions;
10630 wxArrayInt optimizationLineYPositions;
10631
10632#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10633 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10634#endif
10635
603f702b
JS
10636 container->DeleteRange(GetRange());
10637 container->UpdateRanges();
10638 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10639 // Layout() would stop prematurely at the top level.
10640 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 10641
6ccbca24 10642 long caretPos = GetRange().GetStart()-1;
603f702b 10643 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
10644 caretPos --;
10645
7051fa41 10646 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 10647
5912d19e
JS
10648 wxRichTextEvent cmdEvent(
10649 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10650 m_ctrl ? m_ctrl->GetId() : -1);
10651 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10652 cmdEvent.SetRange(GetRange());
10653 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10654 cmdEvent.SetContainer(container);
3e541562 10655
5912d19e
JS
10656 m_buffer->SendEvent(cmdEvent);
10657
5d7836c4
JS
10658 break;
10659 }
10660 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10661 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10662 {
10663 ApplyParagraphs(GetNewParagraphs());
603f702b 10664
c4168888 10665 // Invalidate the whole buffer if there were floating objects
e12b91a3 10666 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10667 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10668 else
10669 {
10670 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10671 // Layout() would stop prematurely at the top level.
10672 container->InvalidateHierarchy(GetRange());
10673 }
603f702b
JS
10674
10675 UpdateAppearance(GetPosition());
10676
10677 wxRichTextEvent cmdEvent(
590a0f8b 10678 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
10679 m_ctrl ? m_ctrl->GetId() : -1);
10680 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10681 cmdEvent.SetRange(GetRange());
10682 cmdEvent.SetPosition(GetRange().GetStart());
10683 cmdEvent.SetContainer(container);
10684
10685 m_buffer->SendEvent(cmdEvent);
10686
10687 break;
10688 }
10689 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10690 {
10691 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10692 if (obj)
10693 {
10694 wxRichTextAttr oldAttr = obj->GetAttributes();
10695 obj->GetAttributes() = m_attributes;
10696 m_attributes = oldAttr;
10697 }
10698
10699 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10700 // Layout() would stop prematurely at the top level.
c4168888 10701 // Invalidate the whole buffer if there were floating objects
e12b91a3 10702 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10703 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10704 else
10705 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10706
10707 UpdateAppearance(GetPosition());
10708
5912d19e
JS
10709 wxRichTextEvent cmdEvent(
10710 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10711 m_ctrl ? m_ctrl->GetId() : -1);
10712 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10713 cmdEvent.SetRange(GetRange());
10714 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10715 cmdEvent.SetContainer(container);
3e541562 10716
5912d19e
JS
10717 m_buffer->SendEvent(cmdEvent);
10718
603f702b
JS
10719 break;
10720 }
10721 case wxRICHTEXT_CHANGE_OBJECT:
10722 {
10723 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10724 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10725 if (obj && m_object)
10726 {
10727 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10728 if (node)
10729 {
10730 wxRichTextObject* obj = node->GetData();
10731 node->SetData(m_object);
10732 m_object = obj;
10733 }
10734 }
10735
10736 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10737 // Layout() would stop prematurely at the top level.
c4168888 10738 // Invalidate the whole buffer if there were floating objects
e12b91a3 10739 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
c4168888
JS
10740 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10741 else
10742 container->InvalidateHierarchy(GetRange());
603f702b
JS
10743
10744 UpdateAppearance(GetPosition());
10745
10746 // TODO: send new kind of modification event
10747
5d7836c4
JS
10748 break;
10749 }
10750 default:
10751 break;
10752 }
10753
10754 return true;
10755}
10756
10757bool wxRichTextAction::Undo()
10758{
10759 m_buffer->Modify(true);
10760
603f702b
JS
10761 wxRichTextParagraphLayoutBox* container = GetContainer();
10762 wxASSERT(container != NULL);
10763 if (!container)
10764 return false;
10765
5d7836c4
JS
10766 switch (m_cmdId)
10767 {
10768 case wxRICHTEXT_INSERT:
10769 {
7051fa41
JS
10770 wxArrayInt optimizationLineCharPositions;
10771 wxArrayInt optimizationLineYPositions;
10772
10773#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10774 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10775#endif
10776
603f702b
JS
10777 container->DeleteRange(GetRange());
10778 container->UpdateRanges();
7c9fdebe 10779
603f702b
JS
10780 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10781 // Layout() would stop prematurely at the top level.
10782 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
10783
10784 long newCaretPosition = GetPosition() - 1;
3e541562 10785
7051fa41 10786 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10787
5912d19e
JS
10788 wxRichTextEvent cmdEvent(
10789 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10790 m_ctrl ? m_ctrl->GetId() : -1);
10791 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10792 cmdEvent.SetRange(GetRange());
10793 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10794 cmdEvent.SetContainer(container);
3e541562 10795
5912d19e
JS
10796 m_buffer->SendEvent(cmdEvent);
10797
5d7836c4
JS
10798 break;
10799 }
10800 case wxRICHTEXT_DELETE:
10801 {
7051fa41
JS
10802 wxArrayInt optimizationLineCharPositions;
10803 wxArrayInt optimizationLineYPositions;
10804
10805#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10806 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10807#endif
10808
603f702b
JS
10809 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10810 container->UpdateRanges();
7c9fdebe 10811
603f702b
JS
10812 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10813 // Layout() would stop prematurely at the top level.
10814 container->InvalidateHierarchy(GetRange());
5d7836c4 10815
7051fa41 10816 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10817
5912d19e
JS
10818 wxRichTextEvent cmdEvent(
10819 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10820 m_ctrl ? m_ctrl->GetId() : -1);
10821 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10822 cmdEvent.SetRange(GetRange());
10823 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10824 cmdEvent.SetContainer(container);
3e541562 10825
5912d19e
JS
10826 m_buffer->SendEvent(cmdEvent);
10827
5d7836c4
JS
10828 break;
10829 }
10830 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10831 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10832 {
10833 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
10834 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10835 // Layout() would stop prematurely at the top level.
10836 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10837
10838 UpdateAppearance(GetPosition());
10839
5912d19e 10840 wxRichTextEvent cmdEvent(
590a0f8b 10841 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
10842 m_ctrl ? m_ctrl->GetId() : -1);
10843 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10844 cmdEvent.SetRange(GetRange());
10845 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10846 cmdEvent.SetContainer(container);
3e541562 10847
5912d19e
JS
10848 m_buffer->SendEvent(cmdEvent);
10849
5d7836c4
JS
10850 break;
10851 }
603f702b
JS
10852 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10853 case wxRICHTEXT_CHANGE_OBJECT:
10854 {
10855 return Do();
10856 }
5d7836c4
JS
10857 default:
10858 break;
10859 }
10860
10861 return true;
10862}
10863
10864/// Update the control appearance
603f702b 10865void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 10866{
603f702b
JS
10867 wxRichTextParagraphLayoutBox* container = GetContainer();
10868 wxASSERT(container != NULL);
10869 if (!container)
10870 return;
10871
5d7836c4
JS
10872 if (m_ctrl)
10873 {
603f702b 10874 m_ctrl->SetFocusObject(container);
5d7836c4 10875 m_ctrl->SetCaretPosition(caretPosition);
603f702b 10876
5d7836c4
JS
10877 if (!m_ctrl->IsFrozen())
10878 {
603f702b
JS
10879 wxRect containerRect = container->GetRect();
10880
2f36e8dc 10881 m_ctrl->LayoutContent();
5d7836c4 10882
603f702b
JS
10883 // Refresh everything if there were floating objects or the container changed size
10884 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
e12b91a3 10885 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
603f702b
JS
10886 {
10887 m_ctrl->Refresh(false);
10888 }
10889 else
10890
10891#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10892 // Find refresh rectangle if we are in a position to optimise refresh
10893 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10894 {
10895 size_t i;
10896
4ba36292
JS
10897 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10898 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
603f702b
JS
10899
10900 // Start/end positions
10901 int firstY = 0;
10902 int lastY = firstVisiblePt.y + clientSize.y;
10903
10904 bool foundEnd = false;
10905
10906 // position offset - how many characters were inserted
10907 int positionOffset = GetRange().GetLength();
10908
10909 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10910 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10911 positionOffset = - positionOffset;
10912
10913 // find the first line which is being drawn at the same position as it was
10914 // before. Since we're talking about a simple insertion, we can assume
10915 // that the rest of the window does not need to be redrawn.
10916
10917 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10918 // Since we support floating layout, we should redraw the whole para instead of just
10919 // the first line touching the invalid range.
10920 if (para)
10921 {
10922 firstY = para->GetPosition().y;
10923 }
10924
10925 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10926 while (node)
10927 {
10928 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10929 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10930 while (node2)
10931 {
10932 wxRichTextLine* line = node2->GetData();
10933 wxPoint pt = line->GetAbsolutePosition();
10934 wxRichTextRange range = line->GetAbsoluteRange();
10935
10936 // we want to find the first line that is in the same position
10937 // as before. This will mean we're at the end of the changed text.
10938
10939 if (pt.y > lastY) // going past the end of the window, no more info
10940 {
10941 node2 = wxRichTextLineList::compatibility_iterator();
10942 node = wxRichTextObjectList::compatibility_iterator();
10943 }
10944 // Detect last line in the buffer
10945 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10946 {
10947 // If deleting text, make sure we refresh below as well as above
10948 if (positionOffset >= 0)
10949 {
10950 foundEnd = true;
10951 lastY = pt.y + line->GetSize().y;
10952 }
10953
10954 node2 = wxRichTextLineList::compatibility_iterator();
10955 node = wxRichTextObjectList::compatibility_iterator();
10956
10957 break;
10958 }
10959 else
10960 {
10961 // search for this line being at the same position as before
10962 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10963 {
10964 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10965 ((*optimizationLineYPositions)[i] == pt.y))
10966 {
10967 // Stop, we're now the same as we were
10968 foundEnd = true;
10969
10970 lastY = pt.y;
10971
10972 node2 = wxRichTextLineList::compatibility_iterator();
10973 node = wxRichTextObjectList::compatibility_iterator();
10974
10975 break;
10976 }
10977 }
10978 }
10979
10980 if (node2)
10981 node2 = node2->GetNext();
10982 }
10983
10984 if (node)
10985 node = node->GetNext();
10986 }
10987
10988 firstY = wxMax(firstVisiblePt.y, firstY);
10989 if (!foundEnd)
10990 lastY = firstVisiblePt.y + clientSize.y;
10991
10992 // Convert to device coordinates
4ba36292 10993 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
603f702b
JS
10994 m_ctrl->RefreshRect(rect);
10995 }
10996 else
1c13f06e 10997#endif
603f702b
JS
10998 m_ctrl->Refresh(false);
10999
11000 m_ctrl->PositionCaret();
4fe83b93
JS
11001
11002 // This causes styles to persist when doing programmatic
11003 // content creation except when Freeze/Thaw is used, so
11004 // disable this and check for the consequences.
11005 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 11006
5d7836c4 11007 if (sendUpdateEvent)
0ec1179b 11008 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 11009 }
7fe8059f 11010 }
5d7836c4
JS
11011}
11012
11013/// Replace the buffer paragraphs with the new ones.
0ca07313 11014void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 11015{
603f702b
JS
11016 wxRichTextParagraphLayoutBox* container = GetContainer();
11017 wxASSERT(container != NULL);
11018 if (!container)
11019 return;
11020
5d7836c4
JS
11021 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
11022 while (node)
11023 {
11024 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
11025 wxASSERT (para != NULL);
11026
11027 // We'll replace the existing paragraph by finding the paragraph at this position,
11028 // delete its node data, and setting a copy as the new node data.
11029 // TODO: make more efficient by simply swapping old and new paragraph objects.
11030
603f702b 11031 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
11032 if (existingPara)
11033 {
603f702b 11034 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
11035 if (bufferParaNode)
11036 {
11037 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 11038 newPara->SetParent(container);
5d7836c4
JS
11039
11040 bufferParaNode->SetData(newPara);
11041
11042 delete existingPara;
11043 }
11044 }
11045
11046 node = node->GetNext();
11047 }
11048}
11049
11050
11051/*!
11052 * wxRichTextRange
11053 * This stores beginning and end positions for a range of data.
11054 */
11055
603f702b
JS
11056WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11057
5d7836c4
JS
11058/// Limit this range to be within 'range'
11059bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11060{
11061 if (m_start < range.m_start)
11062 m_start = range.m_start;
11063
11064 if (m_end > range.m_end)
11065 m_end = range.m_end;
11066
11067 return true;
11068}
11069
11070/*!
11071 * wxRichTextImage implementation
11072 * This object represents an image.
11073 */
11074
bec80f4f 11075IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 11076
24777478 11077wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11078 wxRichTextObject(parent)
5d7836c4 11079{
23698b12 11080 Init();
cdaed652 11081 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
11082 if (charStyle)
11083 SetAttributes(*charStyle);
5d7836c4
JS
11084}
11085
24777478 11086wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 11087 wxRichTextObject(parent)
5d7836c4 11088{
23698b12 11089 Init();
5d7836c4 11090 m_imageBlock = imageBlock;
4f32b3cf
JS
11091 if (charStyle)
11092 SetAttributes(*charStyle);
5d7836c4
JS
11093}
11094
914a4e23
JS
11095wxRichTextImage::~wxRichTextImage()
11096{
11097}
11098
23698b12
JS
11099void wxRichTextImage::Init()
11100{
11101 m_originalImageSize = wxSize(-1, -1);
11102}
11103
cdaed652 11104/// Create a cached image at the required size
914a4e23 11105bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
5d7836c4 11106{
23698b12
JS
11107 if (!m_imageBlock.IsOk())
11108 return false;
11109
11110 // If we have an original image size, use that to compute the cached bitmap size
11111 // instead of loading the image each time. This way we can avoid loading
11112 // the image so long as the new cached bitmap size hasn't changed.
11113
11114 wxImage image;
2798df59 11115 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
cdaed652 11116 {
23698b12 11117 m_imageCache = wxNullBitmap;
ce00f59b 11118
cdaed652
VZ
11119 m_imageBlock.Load(image);
11120 if (!image.IsOk())
11121 return false;
ce00f59b 11122
23698b12
JS
11123 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11124 }
11125
11126 int width = m_originalImageSize.GetWidth();
11127 int height = m_originalImageSize.GetHeight();
11128
11129 int parentWidth = 0;
11130 int parentHeight = 0;
bec80f4f 11131
23698b12
JS
11132 int maxWidth = -1;
11133 int maxHeight = -1;
11134
914a4e23
JS
11135 wxSize sz = parentSize;
11136 if (sz == wxDefaultSize)
23698b12 11137 {
914a4e23
JS
11138 if (GetParent() && GetParent()->GetParent())
11139 sz = GetParent()->GetParent()->GetCachedSize();
11140 }
ab6b1860 11141
914a4e23
JS
11142 if (sz != wxDefaultSize)
11143 {
11144 wxRichTextBuffer* buffer = GetBuffer();
11145 if (buffer)
11146 {
11147 // Find the actual space available when margin is taken into account
23698b12
JS
11148 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11149 marginRect = wxRect(0, 0, sz.x, sz.y);
914a4e23
JS
11150 if (GetParent() && GetParent()->GetParent())
11151 {
11152 buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11153 sz = contentRect.GetSize();
11154 }
23698b12 11155
914a4e23
JS
11156 // Use a minimum size to stop images becoming very small
11157 parentWidth = wxMax(100, sz.GetWidth());
11158 parentHeight = wxMax(100, sz.GetHeight());
23698b12 11159
914a4e23
JS
11160 if (buffer->GetRichTextCtrl())
11161 // Start with a maximum width of the control size, even if not specified by the content,
11162 // to minimize the amount of picture overlapping the right-hand side
11163 maxWidth = parentWidth;
cdaed652 11164 }
23698b12
JS
11165 }
11166
11167 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11168 {
11169 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11170 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11171 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11172 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11173 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11174 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11175 }
11176
11177 // Limit to max width
11178
11179 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11180 {
11181 int mw = -1;
11182
11183 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11184 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11185 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11186 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11187 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11188 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11189
11190 // If we already have a smaller max width due to the constraints of the control size,
11191 // don't use the larger max width.
11192 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11193 maxWidth = mw;
11194 }
11195
11196 if (maxWidth > 0 && width > maxWidth)
11197 width = maxWidth;
11198
11199 // Preserve the aspect ratio
11200 if (width != m_originalImageSize.GetWidth())
11201 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11202
11203 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11204 {
11205 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11206 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11207 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11208 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11209 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11210 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11211
11212 // Preserve the aspect ratio
11213 if (height != m_originalImageSize.GetHeight())
11214 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11215 }
11216
11217 // Limit to max height
11218
11219 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11220 {
11221 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11222 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11223 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11224 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11225 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11226 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11227 }
11228
11229 if (maxHeight > 0 && height > maxHeight)
11230 {
11231 height = maxHeight;
11232
11233 // Preserve the aspect ratio
11234 if (height != m_originalImageSize.GetHeight())
11235 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11236 }
11237
ab6b1860
JS
11238 // Prevent the use of zero size
11239 width = wxMax(1, width);
11240 height = wxMax(1, height);
11241
23698b12
JS
11242 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11243 {
11244 // Do nothing, we didn't need to change the image cache
11245 }
11246 else
11247 {
11248 if (!image.IsOk())
cdaed652 11249 {
23698b12
JS
11250 m_imageBlock.Load(image);
11251 if (!image.IsOk())
11252 return false;
cdaed652 11253 }
5d7836c4 11254
cdaed652
VZ
11255 if (image.GetWidth() == width && image.GetHeight() == height)
11256 m_imageCache = wxBitmap(image);
11257 else
11258 {
11259 // If the original width and height is small, e.g. 400 or below,
11260 // scale up and then down to improve image quality. This can make
11261 // a big difference, with not much performance hit.
11262 int upscaleThreshold = 400;
11263 wxImage img;
11264 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11265 {
11266 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11267 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11268 }
11269 else
11270 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11271 m_imageCache = wxBitmap(img);
11272 }
11273 }
ce00f59b 11274
cdaed652 11275 return m_imageCache.IsOk();
5d7836c4
JS
11276}
11277
5d7836c4 11278/// Draw the item
20d09da5 11279bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 11280{
603f702b
JS
11281 if (!IsShown())
11282 return true;
11283
cdaed652
VZ
11284 // Don't need cached size AFAIK
11285 // wxSize size = GetCachedSize();
11286 if (!LoadImageCache(dc))
5d7836c4 11287 return false;
ce00f59b 11288
8db2e3ef
JS
11289 wxRichTextAttr attr(GetAttributes());
11290 context.ApplyVirtualAttributes(attr, this);
11291
11292 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b 11293
603f702b
JS
11294 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11295 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11296 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 11297 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11298
11299 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 11300
a70eb13e 11301 if (selection.WithinSelection(GetRange().GetStart(), this))
5d7836c4 11302 {
ecb5fbf1
JS
11303 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11304 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 11305 dc.SetLogicalFunction(wxINVERT);
603f702b 11306 dc.DrawRectangle(contentRect);
5d7836c4
JS
11307 dc.SetLogicalFunction(wxCOPY);
11308 }
11309
11310 return true;
11311}
11312
11313/// Lay the item out
8db2e3ef 11314bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 11315{
cdaed652
VZ
11316 if (!LoadImageCache(dc))
11317 return false;
5d7836c4 11318
603f702b
JS
11319 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11320 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11321 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
11322
11323 wxRichTextAttr attr(GetAttributes());
11324 context.ApplyVirtualAttributes(attr, this);
11325
11326 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11327
11328 wxSize overallSize = marginRect.GetSize();
11329
11330 SetCachedSize(overallSize);
11331 SetMaxSize(overallSize);
11332 SetMinSize(overallSize);
cdaed652 11333 SetPosition(rect.GetPosition());
5d7836c4
JS
11334
11335 return true;
11336}
11337
11338/// Get/set the object size for the given range. Returns false if the range
11339/// is invalid for this object.
914a4e23 11340bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), const wxPoint& WXUNUSED(position), const wxSize& parentSize, wxArrayInt* partialExtents) const
5d7836c4
JS
11341{
11342 if (!range.IsWithin(GetRange()))
11343 return false;
11344
914a4e23 11345 if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
31778480 11346 {
cdaed652
VZ
11347 size.x = 0; size.y = 0;
11348 if (partialExtents)
31778480 11349 partialExtents->Add(0);
cdaed652 11350 return false;
31778480 11351 }
ce00f59b 11352
8db2e3ef
JS
11353 wxRichTextAttr attr(GetAttributes());
11354 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11355
603f702b
JS
11356 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11357 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11358 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef 11359 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
11360
11361 wxSize overallSize = marginRect.GetSize();
31778480 11362
cdaed652 11363 if (partialExtents)
603f702b 11364 partialExtents->Add(overallSize.x);
5d7836c4 11365
603f702b 11366 size = overallSize;
5d7836c4
JS
11367
11368 return true;
11369}
11370
603f702b
JS
11371// Get the 'natural' size for an object. For an image, it would be the
11372// image size.
11373wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11374{
11375 wxTextAttrSize size;
11376 if (GetImageCache().IsOk())
11377 {
11378 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11379 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11380 }
11381 return size;
11382}
11383
11384
5d7836c4
JS
11385/// Copy
11386void wxRichTextImage::Copy(const wxRichTextImage& obj)
11387{
bec80f4f 11388 wxRichTextObject::Copy(obj);
59509217 11389
5d7836c4 11390 m_imageBlock = obj.m_imageBlock;
23698b12 11391 m_originalImageSize = obj.m_originalImageSize;
5d7836c4
JS
11392}
11393
cdaed652
VZ
11394/// Edit properties via a GUI
11395bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11396{
603f702b
JS
11397 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11398 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
11399
11400 if (imageDlg.ShowModal() == wxID_OK)
11401 {
603f702b
JS
11402 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11403 // indeterminate in the object.
11404 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
11405 return true;
11406 }
11407 else
11408 return false;
11409}
11410
5d7836c4
JS
11411/*!
11412 * Utilities
11413 *
11414 */
11415
11416/// Compare two attribute objects
24777478 11417bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 11418{
38f833b1 11419 return (attr1 == attr2);
5d7836c4
JS
11420}
11421
44cc96a8
JS
11422/// Compare tabs
11423bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11424{
11425 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
11426 return false;
11427
44cc96a8
JS
11428 size_t i;
11429 for (i = 0; i < tabs1.GetCount(); i++)
11430 {
11431 if (tabs1[i] != tabs2[i])
11432 return false;
11433 }
11434 return true;
11435}
5d7836c4 11436
24777478 11437bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
11438{
11439 return destStyle.Apply(style, compareWith);
11440}
5d7836c4 11441
44cc96a8 11442// Remove attributes
24777478 11443bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 11444{
24777478 11445 return destStyle.RemoveStyle(style);
44cc96a8 11446}
5d7836c4 11447
44cc96a8
JS
11448/// Combine two bitlists, specifying the bits of interest with separate flags.
11449bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11450{
24777478 11451 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 11452}
5d7836c4 11453
44cc96a8
JS
11454/// Compare two bitlists
11455bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11456{
24777478 11457 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 11458}
38f833b1 11459
44cc96a8 11460/// Split into paragraph and character styles
24777478 11461bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 11462{
24777478 11463 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 11464}
5d7836c4 11465
44cc96a8
JS
11466/// Convert a decimal to Roman numerals
11467wxString wxRichTextDecimalToRoman(long n)
11468{
11469 static wxArrayInt decimalNumbers;
11470 static wxArrayString romanNumbers;
5d7836c4 11471
44cc96a8
JS
11472 // Clean up arrays
11473 if (n == -1)
11474 {
11475 decimalNumbers.Clear();
11476 romanNumbers.Clear();
11477 return wxEmptyString;
11478 }
5d7836c4 11479
44cc96a8
JS
11480 if (decimalNumbers.GetCount() == 0)
11481 {
11482 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 11483
44cc96a8
JS
11484 wxRichTextAddDecRom(1000, wxT("M"));
11485 wxRichTextAddDecRom(900, wxT("CM"));
11486 wxRichTextAddDecRom(500, wxT("D"));
11487 wxRichTextAddDecRom(400, wxT("CD"));
11488 wxRichTextAddDecRom(100, wxT("C"));
11489 wxRichTextAddDecRom(90, wxT("XC"));
11490 wxRichTextAddDecRom(50, wxT("L"));
11491 wxRichTextAddDecRom(40, wxT("XL"));
11492 wxRichTextAddDecRom(10, wxT("X"));
11493 wxRichTextAddDecRom(9, wxT("IX"));
11494 wxRichTextAddDecRom(5, wxT("V"));
11495 wxRichTextAddDecRom(4, wxT("IV"));
11496 wxRichTextAddDecRom(1, wxT("I"));
11497 }
5d7836c4 11498
44cc96a8
JS
11499 int i = 0;
11500 wxString roman;
ea160b2e 11501
44cc96a8 11502 while (n > 0 && i < 13)
42688aea 11503 {
44cc96a8
JS
11504 if (n >= decimalNumbers[i])
11505 {
11506 n -= decimalNumbers[i];
11507 roman += romanNumbers[i];
11508 }
11509 else
11510 {
11511 i ++;
11512 }
42688aea 11513 }
44cc96a8
JS
11514 if (roman.IsEmpty())
11515 roman = wxT("0");
11516 return roman;
11517}
42688aea 11518
44cc96a8
JS
11519/*!
11520 * wxRichTextFileHandler
11521 * Base class for file handlers
11522 */
4d6d8bf4 11523
44cc96a8 11524IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 11525
44cc96a8
JS
11526#if wxUSE_FFILE && wxUSE_STREAMS
11527bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 11528{
44cc96a8 11529 wxFFileInputStream stream(filename);
a1b806b9 11530 if (stream.IsOk())
44cc96a8 11531 return LoadFile(buffer, stream);
5d7836c4 11532
44cc96a8
JS
11533 return false;
11534}
5d7836c4 11535
44cc96a8
JS
11536bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11537{
11538 wxFFileOutputStream stream(filename);
a1b806b9 11539 if (stream.IsOk())
44cc96a8 11540 return SaveFile(buffer, stream);
5d7836c4 11541
44cc96a8
JS
11542 return false;
11543}
11544#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 11545
44cc96a8
JS
11546/// Can we handle this filename (if using files)? By default, checks the extension.
11547bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11548{
11549 wxString path, file, ext;
a51e601e 11550 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 11551
44cc96a8
JS
11552 return (ext.Lower() == GetExtension());
11553}
5d7836c4 11554
44cc96a8
JS
11555/*!
11556 * wxRichTextTextHandler
11557 * Plain text handler
11558 */
5d7836c4 11559
44cc96a8 11560IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 11561
44cc96a8
JS
11562#if wxUSE_STREAMS
11563bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11564{
11565 if (!stream.IsOk())
797e38dd
JS
11566 return false;
11567
44cc96a8
JS
11568 wxString str;
11569 int lastCh = 0;
5d7836c4 11570
44cc96a8
JS
11571 while (!stream.Eof())
11572 {
11573 int ch = stream.GetC();
5d7836c4 11574
44cc96a8
JS
11575 if (!stream.Eof())
11576 {
11577 if (ch == 10 && lastCh != 13)
11578 str += wxT('\n');
5d7836c4 11579
44cc96a8
JS
11580 if (ch > 0 && ch != 10)
11581 str += wxChar(ch);
5d7836c4 11582
44cc96a8
JS
11583 lastCh = ch;
11584 }
11585 }
5d7836c4 11586
44cc96a8
JS
11587 buffer->ResetAndClearCommands();
11588 buffer->Clear();
11589 buffer->AddParagraphs(str);
11590 buffer->UpdateRanges();
5d7836c4 11591
44cc96a8
JS
11592 return true;
11593}
5d7836c4 11594
44cc96a8
JS
11595bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11596{
11597 if (!stream.IsOk())
5d7836c4
JS
11598 return false;
11599
44cc96a8 11600 wxString text = buffer->GetText();
38f833b1 11601
44cc96a8
JS
11602 wxString newLine = wxRichTextLineBreakChar;
11603 text.Replace(newLine, wxT("\n"));
5d7836c4 11604
44cc96a8 11605 wxCharBuffer buf = text.ToAscii();
5d7836c4 11606
44cc96a8
JS
11607 stream.Write((const char*) buf, text.length());
11608 return true;
11609}
11610#endif // wxUSE_STREAMS
5d7836c4 11611
44cc96a8
JS
11612/*
11613 * Stores information about an image, in binary in-memory form
11614 */
59509217 11615
44cc96a8
JS
11616wxRichTextImageBlock::wxRichTextImageBlock()
11617{
11618 Init();
11619}
5d7836c4 11620
44cc96a8
JS
11621wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11622{
11623 Init();
11624 Copy(block);
11625}
ea160b2e 11626
44cc96a8
JS
11627wxRichTextImageBlock::~wxRichTextImageBlock()
11628{
5276b0a5 11629 wxDELETEA(m_data);
5d7836c4
JS
11630}
11631
44cc96a8 11632void wxRichTextImageBlock::Init()
5d7836c4
JS
11633{
11634 m_data = NULL;
11635 m_dataSize = 0;
d75a69e8 11636 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11637}
11638
11639void wxRichTextImageBlock::Clear()
11640{
5276b0a5 11641 wxDELETEA(m_data);
5d7836c4 11642 m_dataSize = 0;
d75a69e8 11643 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11644}
11645
11646
11647// Load the original image into a memory block.
11648// If the image is not a JPEG, we must convert it into a JPEG
11649// to conserve space.
11650// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11651// load the image a 2nd time.
11652
d75a69e8
FM
11653bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11654 wxImage& image, bool convertToJPEG)
5d7836c4
JS
11655{
11656 m_imageType = imageType;
11657
11658 wxString filenameToRead(filename);
7fe8059f 11659 bool removeFile = false;
5d7836c4 11660
62891c87 11661 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11662 return false; // Could not determine image type
5d7836c4
JS
11663
11664 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11665 {
a51e601e
FM
11666 wxString tempFile =
11667 wxFileName::CreateTempFileName(_("image"));
5d7836c4 11668
a51e601e 11669 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11670
11671 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11672 filenameToRead = tempFile;
7fe8059f 11673 removeFile = true;
5d7836c4
JS
11674
11675 m_imageType = wxBITMAP_TYPE_JPEG;
11676 }
11677 wxFile file;
11678 if (!file.Open(filenameToRead))
7fe8059f 11679 return false;
5d7836c4
JS
11680
11681 m_dataSize = (size_t) file.Length();
11682 file.Close();
11683
11684 if (m_data)
11685 delete[] m_data;
11686 m_data = ReadBlock(filenameToRead, m_dataSize);
11687
11688 if (removeFile)
11689 wxRemoveFile(filenameToRead);
11690
11691 return (m_data != NULL);
11692}
11693
11694// Make an image block from the wxImage in the given
11695// format.
d75a69e8 11696bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 11697{
5d7836c4
JS
11698 image.SetOption(wxT("quality"), quality);
11699
62891c87 11700 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11701 return false; // Could not determine image type
5d7836c4 11702
cdaed652
VZ
11703 return DoMakeImageBlock(image, imageType);
11704}
11705
11706// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11707bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11708{
11709 if (imageType == wxBITMAP_TYPE_INVALID)
11710 return false; // Could not determine image type
ce00f59b 11711
cdaed652
VZ
11712 return DoMakeImageBlock(image, imageType);
11713}
7fe8059f 11714
cdaed652
VZ
11715// Makes the image block
11716bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11717{
11718 wxMemoryOutputStream memStream;
11719 if (!image.SaveFile(memStream, imageType))
5d7836c4 11720 {
7fe8059f 11721 return false;
5d7836c4 11722 }
ce00f59b 11723
cdaed652
VZ
11724 unsigned char* block = new unsigned char[memStream.GetSize()];
11725 if (!block)
377c1ba4 11726 return false;
ce00f59b 11727
5d7836c4
JS
11728 if (m_data)
11729 delete[] m_data;
cdaed652 11730 m_data = block;
ce00f59b
VZ
11731
11732 m_imageType = imageType;
cdaed652 11733 m_dataSize = memStream.GetSize();
5d7836c4 11734
cdaed652 11735 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
11736
11737 return (m_data != NULL);
11738}
11739
5d7836c4
JS
11740// Write to a file
11741bool wxRichTextImageBlock::Write(const wxString& filename)
11742{
11743 return WriteBlock(filename, m_data, m_dataSize);
11744}
11745
11746void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11747{
11748 m_imageType = block.m_imageType;
5276b0a5 11749 wxDELETEA(m_data);
5d7836c4
JS
11750 m_dataSize = block.m_dataSize;
11751 if (m_dataSize == 0)
11752 return;
11753
11754 m_data = new unsigned char[m_dataSize];
11755 unsigned int i;
11756 for (i = 0; i < m_dataSize; i++)
11757 m_data[i] = block.m_data[i];
11758}
11759
11760//// Operators
11761void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11762{
11763 Copy(block);
11764}
11765
11766// Load a wxImage from the block
11767bool wxRichTextImageBlock::Load(wxImage& image)
11768{
11769 if (!m_data)
7fe8059f 11770 return false;
5d7836c4
JS
11771
11772 // Read in the image.
0ca07313 11773#if wxUSE_STREAMS
5d7836c4
JS
11774 wxMemoryInputStream mstream(m_data, m_dataSize);
11775 bool success = image.LoadFile(mstream, GetImageType());
11776#else
a51e601e
FM
11777 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11778 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11779
11780 if (!WriteBlock(tempFile, m_data, m_dataSize))
11781 {
7fe8059f 11782 return false;
5d7836c4
JS
11783 }
11784 success = image.LoadFile(tempFile, GetImageType());
11785 wxRemoveFile(tempFile);
11786#endif
11787
11788 return success;
11789}
11790
11791// Write data in hex to a stream
11792bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11793{
4dc7ae1a
JS
11794 if (m_dataSize == 0)
11795 return true;
11796
11797 int bufSize = 100000;
a3c12576
JS
11798 if (int(2*m_dataSize) < bufSize)
11799 bufSize = 2*m_dataSize;
4dc7ae1a 11800 char* buf = new char[bufSize+1];
351c0647
JS
11801
11802 int left = m_dataSize;
11803 int n, i, j;
11804 j = 0;
11805 while (left > 0)
5d7836c4 11806 {
351c0647
JS
11807 if (left*2 > bufSize)
11808 {
11809 n = bufSize; left -= (bufSize/2);
11810 }
11811 else
11812 {
11813 n = left*2; left = 0;
11814 }
7fe8059f 11815
351c0647
JS
11816 char* b = buf;
11817 for (i = 0; i < (n/2); i++)
11818 {
f728025e 11819 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
11820 b += 2; j ++;
11821 }
5d7836c4 11822
351c0647
JS
11823 buf[n] = 0;
11824 stream.Write((const char*) buf, n);
11825 }
4dc7ae1a 11826 delete[] buf;
5d7836c4
JS
11827 return true;
11828}
11829
11830// Read data in hex from a stream
d75a69e8 11831bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
11832{
11833 int dataSize = length/2;
11834
11835 if (m_data)
11836 delete[] m_data;
11837
046fce47
FM
11838 // create a null terminated temporary string:
11839 char str[3];
11840 str[2] = '\0';
11841
5d7836c4
JS
11842 m_data = new unsigned char[dataSize];
11843 int i;
11844 for (i = 0; i < dataSize; i ++)
11845 {
c9f78968
VS
11846 str[0] = (char)stream.GetC();
11847 str[1] = (char)stream.GetC();
5d7836c4 11848
a9465653 11849 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
11850 }
11851
11852 m_dataSize = dataSize;
11853 m_imageType = imageType;
11854
11855 return true;
11856}
11857
5d7836c4
JS
11858// Allocate and read from stream as a block of memory
11859unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11860{
11861 unsigned char* block = new unsigned char[size];
11862 if (!block)
11863 return NULL;
11864
11865 stream.Read(block, size);
11866
11867 return block;
11868}
11869
11870unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11871{
11872 wxFileInputStream stream(filename);
a1b806b9 11873 if (!stream.IsOk())
5d7836c4
JS
11874 return NULL;
11875
11876 return ReadBlock(stream, size);
11877}
11878
11879// Write memory block to stream
11880bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11881{
11882 stream.Write((void*) block, size);
11883 return stream.IsOk();
11884
11885}
11886
11887// Write memory block to file
11888bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11889{
11890 wxFileOutputStream outStream(filename);
a1b806b9 11891 if (!outStream.IsOk())
7fe8059f 11892 return false;
5d7836c4
JS
11893
11894 return WriteBlock(outStream, block, size);
11895}
11896
d2d0adc7
JS
11897// Gets the extension for the block's type
11898wxString wxRichTextImageBlock::GetExtension() const
11899{
11900 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11901 if (handler)
11902 return handler->GetExtension();
11903 else
11904 return wxEmptyString;
11905}
11906
0ca07313
JS
11907#if wxUSE_DATAOBJ
11908
11909/*!
11910 * The data object for a wxRichTextBuffer
11911 */
11912
11913const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11914
11915wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11916{
11917 m_richTextBuffer = richTextBuffer;
11918
11919 // this string should uniquely identify our format, but is otherwise
11920 // arbitrary
11921 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11922
11923 SetFormat(m_formatRichTextBuffer);
11924}
11925
11926wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11927{
11928 delete m_richTextBuffer;
11929}
11930
11931// after a call to this function, the richTextBuffer is owned by the caller and it
11932// is responsible for deleting it!
11933wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11934{
11935 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11936 m_richTextBuffer = NULL;
11937
11938 return richTextBuffer;
11939}
11940
11941wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11942{
11943 return m_formatRichTextBuffer;
11944}
11945
11946size_t wxRichTextBufferDataObject::GetDataSize() const
11947{
11948 if (!m_richTextBuffer)
11949 return 0;
11950
11951 wxString bufXML;
11952
11953 {
11954 wxStringOutputStream stream(& bufXML);
11955 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11956 {
11957 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11958 return 0;
11959 }
11960 }
11961
11962#if wxUSE_UNICODE
11963 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11964 return strlen(buffer) + 1;
11965#else
11966 return bufXML.Length()+1;
11967#endif
11968}
11969
11970bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11971{
11972 if (!pBuf || !m_richTextBuffer)
11973 return false;
11974
11975 wxString bufXML;
11976
11977 {
11978 wxStringOutputStream stream(& bufXML);
11979 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11980 {
11981 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11982 return 0;
11983 }
11984 }
11985
11986#if wxUSE_UNICODE
11987 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11988 size_t len = strlen(buffer);
11989 memcpy((char*) pBuf, (const char*) buffer, len);
11990 ((char*) pBuf)[len] = 0;
11991#else
11992 size_t len = bufXML.Length();
11993 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11994 ((char*) pBuf)[len] = 0;
11995#endif
11996
11997 return true;
11998}
11999
12000bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
12001{
5276b0a5 12002 wxDELETE(m_richTextBuffer);
0ca07313
JS
12003
12004 wxString bufXML((const char*) buf, wxConvUTF8);
12005
12006 m_richTextBuffer = new wxRichTextBuffer;
12007
12008 wxStringInputStream stream(bufXML);
12009 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
12010 {
12011 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12012
5276b0a5 12013 wxDELETE(m_richTextBuffer);
0ca07313
JS
12014
12015 return false;
12016 }
12017 return true;
12018}
12019
12020#endif
12021 // wxUSE_DATAOBJ
12022
44cc96a8
JS
12023
12024/*
12025 * wxRichTextFontTable
12026 * Manages quick access to a pool of fonts for rendering rich text
12027 */
12028
d65381ac 12029WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
12030
12031class wxRichTextFontTableData: public wxObjectRefData
12032{
12033public:
12034 wxRichTextFontTableData() {}
12035
32423dd8 12036 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
44cc96a8
JS
12037
12038 wxRichTextFontTableHashMap m_hashMap;
12039};
12040
32423dd8 12041wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
44cc96a8
JS
12042{
12043 wxString facename(fontSpec.GetFontFaceName());
44cc96a8 12044
32423dd8
JS
12045 int fontSize = fontSpec.GetFontSize();
12046 if (fontScale != 1.0)
12047 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12048
12049 wxString units;
12050 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12051 units = wxT("px");
12052 else
12053 units = wxT("pt");
12054 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12055 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12056 facename.c_str(), (int) fontSpec.GetFontEncoding());
12057
12058 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
44cc96a8
JS
12059 if ( entry == m_hashMap.end() )
12060 {
32423dd8
JS
12061 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12062 {
b42e4b3e 12063 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
32423dd8
JS
12064 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12065 font.SetStrikethrough(true);
12066 m_hashMap[spec] = font;
12067 return font;
12068 }
12069 else
12070 {
5a0e33af 12071 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
32423dd8
JS
12072 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12073 font.SetStrikethrough(true);
12074
12075 m_hashMap[spec] = font;
12076 return font;
12077 }
44cc96a8
JS
12078 }
12079 else
12080 {
12081 return entry->second;
12082 }
12083}
12084
12085IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12086
12087wxRichTextFontTable::wxRichTextFontTable()
12088{
12089 m_refData = new wxRichTextFontTableData;
32423dd8 12090 m_fontScale = 1.0;
44cc96a8
JS
12091}
12092
12093wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 12094 : wxObject()
44cc96a8
JS
12095{
12096 (*this) = table;
12097}
12098
12099wxRichTextFontTable::~wxRichTextFontTable()
12100{
12101 UnRef();
12102}
12103
12104bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12105{
12106 return (m_refData == table.m_refData);
12107}
12108
12109void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12110{
12111 Ref(table);
32423dd8 12112 m_fontScale = table.m_fontScale;
44cc96a8
JS
12113}
12114
24777478 12115wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
12116{
12117 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12118 if (data)
32423dd8 12119 return data->FindFont(fontSpec, m_fontScale);
44cc96a8
JS
12120 else
12121 return wxFont();
12122}
12123
12124void wxRichTextFontTable::Clear()
12125{
12126 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12127 if (data)
12128 data->m_hashMap.clear();
12129}
12130
32423dd8
JS
12131void wxRichTextFontTable::SetFontScale(double fontScale)
12132{
12133 if (fontScale != m_fontScale)
12134 Clear();
12135 m_fontScale = fontScale;
12136}
12137
24777478
JS
12138// wxTextBoxAttr
12139
24777478
JS
12140void wxTextBoxAttr::Reset()
12141{
12142 m_flags = 0;
603f702b
JS
12143 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12144 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12145 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12146 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 12147 m_boxStyleName = wxEmptyString;
bec80f4f 12148
24777478
JS
12149 m_margins.Reset();
12150 m_padding.Reset();
12151 m_position.Reset();
12152
603f702b 12153 m_size.Reset();
303f0be7
JS
12154 m_minSize.Reset();
12155 m_maxSize.Reset();
24777478
JS
12156
12157 m_border.Reset();
12158 m_outline.Reset();
12159}
12160
12161// Equality test
12162bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12163{
12164 return (
12165 m_flags == attr.m_flags &&
12166 m_floatMode == attr.m_floatMode &&
12167 m_clearMode == attr.m_clearMode &&
12168 m_collapseMode == attr.m_collapseMode &&
603f702b 12169 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 12170
24777478
JS
12171 m_margins == attr.m_margins &&
12172 m_padding == attr.m_padding &&
12173 m_position == attr.m_position &&
12174
603f702b 12175 m_size == attr.m_size &&
303f0be7
JS
12176 m_minSize == attr.m_minSize &&
12177 m_maxSize == attr.m_maxSize &&
24777478
JS
12178
12179 m_border == attr.m_border &&
2f987d83
JS
12180 m_outline == attr.m_outline &&
12181
12182 m_boxStyleName == attr.m_boxStyleName
24777478
JS
12183 );
12184}
12185
12186// Partial equality test
32423dd8 12187bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
24777478 12188{
32423dd8
JS
12189 if (!weakTest &&
12190 ((!HasFloatMode() && attr.HasFloatMode()) ||
12191 (!HasClearMode() && attr.HasClearMode()) ||
12192 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12193 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12194 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12195 {
12196 return false;
12197 }
24777478
JS
12198 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12199 return false;
12200
12201 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12202 return false;
12203
12204 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12205 return false;
12206
603f702b
JS
12207 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12208 return false;
12209
2f987d83
JS
12210 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12211 return false;
12212
24777478
JS
12213 // Position
12214
32423dd8 12215 if (!m_position.EqPartial(attr.m_position, weakTest))
24777478
JS
12216 return false;
12217
303f0be7
JS
12218 // Size
12219
32423dd8 12220 if (!m_size.EqPartial(attr.m_size, weakTest))
303f0be7 12221 return false;
32423dd8 12222 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
303f0be7 12223 return false;
32423dd8 12224 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
303f0be7
JS
12225 return false;
12226
24777478
JS
12227 // Margins
12228
32423dd8 12229 if (!m_margins.EqPartial(attr.m_margins, weakTest))
24777478
JS
12230 return false;
12231
12232 // Padding
12233
32423dd8 12234 if (!m_padding.EqPartial(attr.m_padding, weakTest))
24777478
JS
12235 return false;
12236
12237 // Border
12238
32423dd8 12239 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
24777478
JS
12240 return false;
12241
12242 // Outline
12243
32423dd8 12244 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
24777478
JS
12245 return false;
12246
12247 return true;
12248}
12249
12250// Merges the given attributes. If compareWith
12251// is non-NULL, then it will be used to mask out those attributes that are the same in style
12252// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12253bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12254{
12255 if (attr.HasFloatMode())
12256 {
12257 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12258 SetFloatMode(attr.GetFloatMode());
12259 }
12260
12261 if (attr.HasClearMode())
12262 {
12263 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12264 SetClearMode(attr.GetClearMode());
12265 }
12266
12267 if (attr.HasCollapseBorders())
12268 {
12269 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
12270 SetCollapseBorders(attr.GetCollapseBorders());
12271 }
12272
12273 if (attr.HasVerticalAlignment())
12274 {
12275 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12276 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 12277 }
bec80f4f 12278
2f987d83
JS
12279 if (attr.HasBoxStyleName())
12280 {
12281 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12282 SetBoxStyleName(attr.GetBoxStyleName());
12283 }
12284
bec80f4f
JS
12285 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12286 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12287 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 12288
603f702b 12289 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
12290 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12291 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 12292
bec80f4f
JS
12293 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12294 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
12295
12296 return true;
12297}
12298
12299// Remove specified attributes from this object
12300bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12301{
12302 if (attr.HasFloatMode())
12303 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12304
12305 if (attr.HasClearMode())
12306 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12307
12308 if (attr.HasCollapseBorders())
12309 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12310
603f702b
JS
12311 if (attr.HasVerticalAlignment())
12312 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12313
2f987d83
JS
12314 if (attr.HasBoxStyleName())
12315 {
12316 SetBoxStyleName(wxEmptyString);
12317 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12318 }
12319
24777478
JS
12320 m_margins.RemoveStyle(attr.m_margins);
12321 m_padding.RemoveStyle(attr.m_padding);
12322 m_position.RemoveStyle(attr.m_position);
12323
603f702b 12324 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
12325 m_minSize.RemoveStyle(attr.m_minSize);
12326 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
12327
12328 m_border.RemoveStyle(attr.m_border);
12329 m_outline.RemoveStyle(attr.m_outline);
12330
12331 return true;
12332}
12333
12334// Collects the attributes that are common to a range of content, building up a note of
12335// which attributes are absent in some objects and which clash in some objects.
12336void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12337{
12338 if (attr.HasFloatMode())
12339 {
12340 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12341 {
12342 if (HasFloatMode())
12343 {
12344 if (GetFloatMode() != attr.GetFloatMode())
12345 {
12346 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12347 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12348 }
12349 }
12350 else
12351 SetFloatMode(attr.GetFloatMode());
12352 }
12353 }
12354 else
12355 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 12356
24777478
JS
12357 if (attr.HasClearMode())
12358 {
12359 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12360 {
12361 if (HasClearMode())
12362 {
12363 if (GetClearMode() != attr.GetClearMode())
12364 {
12365 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12366 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12367 }
12368 }
12369 else
12370 SetClearMode(attr.GetClearMode());
12371 }
12372 }
12373 else
12374 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12375
12376 if (attr.HasCollapseBorders())
12377 {
12378 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12379 {
12380 if (HasCollapseBorders())
12381 {
12382 if (GetCollapseBorders() != attr.GetCollapseBorders())
12383 {
12384 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12385 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12386 }
12387 }
12388 else
12389 SetCollapseBorders(attr.GetCollapseBorders());
12390 }
12391 }
12392 else
12393 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 12394
603f702b
JS
12395 if (attr.HasVerticalAlignment())
12396 {
12397 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12398 {
12399 if (HasVerticalAlignment())
12400 {
12401 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12402 {
12403 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12404 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12405 }
12406 }
12407 else
12408 SetVerticalAlignment(attr.GetVerticalAlignment());
12409 }
12410 }
12411 else
12412 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12413
2f987d83
JS
12414 if (attr.HasBoxStyleName())
12415 {
12416 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12417 {
12418 if (HasBoxStyleName())
12419 {
12420 if (GetBoxStyleName() != attr.GetBoxStyleName())
12421 {
12422 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12423 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12424 }
12425 }
12426 else
12427 SetBoxStyleName(attr.GetBoxStyleName());
12428 }
12429 }
12430 else
12431 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12432
24777478
JS
12433 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12434 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12435 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12436
603f702b 12437 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
12438 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12439 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
12440
12441 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12442 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12443}
12444
eb3d8a33
JS
12445bool wxTextBoxAttr::IsDefault() const
12446{
12447 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 12448 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
12449 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12450}
12451
24777478
JS
12452// wxRichTextAttr
12453
12454void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12455{
bec80f4f
JS
12456 wxTextAttr::Copy(attr);
12457
24777478
JS
12458 m_textBoxAttr = attr.m_textBoxAttr;
12459}
12460
12461bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12462{
12463 if (!(wxTextAttr::operator==(attr)))
12464 return false;
bec80f4f 12465
24777478
JS
12466 return (m_textBoxAttr == attr.m_textBoxAttr);
12467}
12468
32423dd8
JS
12469// Partial equality test
12470bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
24777478 12471{
32423dd8 12472 if (!(wxTextAttr::EqPartial(attr, weakTest)))
24777478 12473 return false;
bec80f4f 12474
32423dd8 12475 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
24777478
JS
12476}
12477
12478// Merges the given attributes. If compareWith
12479// is non-NULL, then it will be used to mask out those attributes that are the same in style
12480// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12481bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12482{
12483 wxTextAttr::Apply(style, compareWith);
12484
12485 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12486}
12487
12488// Remove specified attributes from this object
12489bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12490{
12491 wxTextAttr::RemoveStyle(*this, attr);
12492
12493 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12494}
12495
12496// Collects the attributes that are common to a range of content, building up a note of
12497// which attributes are absent in some objects and which clash in some objects.
12498void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12499{
12500 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 12501
24777478
JS
12502 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12503}
12504
12505// Partial equality test
32423dd8 12506bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
24777478 12507{
32423dd8
JS
12508 if (!weakTest &&
12509 ((!HasStyle() && border.HasStyle()) ||
12510 (!HasColour() && border.HasColour()) ||
12511 (!HasWidth() && border.HasWidth())))
12512 {
12513 return false;
12514 }
12515
12516 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
24777478
JS
12517 return false;
12518
32423dd8 12519 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
24777478
JS
12520 return false;
12521
32423dd8 12522 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
24777478
JS
12523 return false;
12524
12525 return true;
12526}
12527
12528// Apply border to 'this', but not if the same as compareWith
bec80f4f 12529bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
12530{
12531 if (border.HasStyle())
12532 {
12533 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12534 SetStyle(border.GetStyle());
12535 }
12536 if (border.HasColour())
12537 {
12538 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12539 SetColour(border.GetColourLong());
12540 }
12541 if (border.HasWidth())
12542 {
12543 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12544 SetWidth(border.GetWidth());
12545 }
12546
12547 return true;
12548}
12549
12550// Remove specified attributes from this object
bec80f4f 12551bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
12552{
12553 if (attr.HasStyle() && HasStyle())
12554 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12555 if (attr.HasColour() && HasColour())
12556 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12557 if (attr.HasWidth() && HasWidth())
12558 m_borderWidth.Reset();
12559
12560 return true;
12561}
12562
12563// Collects the attributes that are common to a range of content, building up a note of
12564// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12565void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
12566{
12567 if (attr.HasStyle())
12568 {
12569 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12570 {
12571 if (HasStyle())
12572 {
12573 if (GetStyle() != attr.GetStyle())
12574 {
12575 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12576 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12577 }
12578 }
12579 else
12580 SetStyle(attr.GetStyle());
12581 }
12582 }
12583 else
12584 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12585
12586 if (attr.HasColour())
12587 {
12588 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12589 {
12590 if (HasColour())
12591 {
12592 if (GetColour() != attr.GetColour())
12593 {
12594 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12595 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12596 }
12597 }
12598 else
12599 SetColour(attr.GetColourLong());
12600 }
12601 }
12602 else
12603 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 12604
24777478
JS
12605 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12606}
12607
12608// Partial equality test
32423dd8 12609bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
24777478 12610{
32423dd8
JS
12611 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12612 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
24777478
JS
12613}
12614
12615// Apply border to 'this', but not if the same as compareWith
bec80f4f 12616bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 12617{
bec80f4f
JS
12618 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12619 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12620 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12621 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
12622 return true;
12623}
12624
12625// Remove specified attributes from this object
bec80f4f 12626bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
12627{
12628 m_left.RemoveStyle(attr.m_left);
12629 m_right.RemoveStyle(attr.m_right);
12630 m_top.RemoveStyle(attr.m_top);
12631 m_bottom.RemoveStyle(attr.m_bottom);
12632 return true;
12633}
12634
12635// Collects the attributes that are common to a range of content, building up a note of
12636// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12637void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
12638{
12639 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12640 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12641 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12642 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12643}
12644
12645// Set style of all borders
bec80f4f 12646void wxTextAttrBorders::SetStyle(int style)
24777478
JS
12647{
12648 m_left.SetStyle(style);
12649 m_right.SetStyle(style);
12650 m_top.SetStyle(style);
12651 m_bottom.SetStyle(style);
12652}
12653
12654// Set colour of all borders
bec80f4f 12655void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
12656{
12657 m_left.SetColour(colour);
12658 m_right.SetColour(colour);
12659 m_top.SetColour(colour);
12660 m_bottom.SetColour(colour);
12661}
12662
bec80f4f 12663void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
12664{
12665 m_left.SetColour(colour);
12666 m_right.SetColour(colour);
12667 m_top.SetColour(colour);
12668 m_bottom.SetColour(colour);
12669}
12670
12671// Set width of all borders
bec80f4f 12672void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
12673{
12674 m_left.SetWidth(width);
12675 m_right.SetWidth(width);
12676 m_top.SetWidth(width);
12677 m_bottom.SetWidth(width);
12678}
12679
12680// Partial equality test
32423dd8 12681bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
24777478 12682{
32423dd8
JS
12683 if (!weakTest && !IsValid() && dim.IsValid())
12684 return false;
12685
603f702b 12686 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
12687 return false;
12688 else
12689 return true;
12690}
12691
12692bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12693{
603f702b 12694 if (dim.IsValid())
24777478
JS
12695 {
12696 if (!(compareWith && dim == (*compareWith)))
12697 (*this) = dim;
12698 }
12699
12700 return true;
12701}
12702
12703// Collects the attributes that are common to a range of content, building up a note of
12704// which attributes are absent in some objects and which clash in some objects.
12705void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12706{
603f702b 12707 if (attr.IsValid())
24777478 12708 {
603f702b 12709 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 12710 {
603f702b 12711 if (IsValid())
24777478
JS
12712 {
12713 if (!((*this) == attr))
12714 {
603f702b
JS
12715 clashingAttr.SetValid(true);
12716 SetValid(false);
24777478
JS
12717 }
12718 }
12719 else
12720 (*this) = attr;
12721 }
12722 }
12723 else
603f702b 12724 absentAttr.SetValid(true);
24777478
JS
12725}
12726
8995db52
JS
12727wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12728{
12729 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12730}
12731
12732wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12733{
12734 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12735}
12736
bec80f4f
JS
12737int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12738{
12739 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12740}
12741
12742int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12743{
12744 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12745}
12746
12747int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12748{
12749 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12750 return ConvertTenthsMMToPixels(dim.GetValue());
12751 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12752 return dim.GetValue();
12753 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12754 {
12755 wxASSERT(m_parentSize != wxDefaultSize);
12756 if (direction == wxHORIZONTAL)
12757 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12758 else
12759 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12760 }
12761 else
12762 {
12763 wxASSERT(false);
12764 return 0;
12765 }
12766}
12767
12768int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12769{
12770 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12771 return dim.GetValue();
12772 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12773 return ConvertPixelsToTenthsMM(dim.GetValue());
12774 else
12775 {
12776 wxASSERT(false);
12777 return 0;
12778 }
12779}
12780
24777478 12781// Partial equality test
32423dd8 12782bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
24777478 12783{
32423dd8 12784 if (!m_left.EqPartial(dims.m_left, weakTest))
24777478
JS
12785 return false;
12786
32423dd8 12787 if (!m_right.EqPartial(dims.m_right, weakTest))
24777478
JS
12788 return false;
12789
32423dd8 12790 if (!m_top.EqPartial(dims.m_top, weakTest))
24777478
JS
12791 return false;
12792
32423dd8 12793 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
24777478
JS
12794 return false;
12795
12796 return true;
12797}
12798
12799// Apply border to 'this', but not if the same as compareWith
bec80f4f 12800bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
12801{
12802 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12803 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12804 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12805 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12806
12807 return true;
12808}
12809
12810// Remove specified attributes from this object
bec80f4f 12811bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 12812{
603f702b 12813 if (attr.m_left.IsValid())
24777478 12814 m_left.Reset();
603f702b 12815 if (attr.m_right.IsValid())
24777478 12816 m_right.Reset();
603f702b 12817 if (attr.m_top.IsValid())
24777478 12818 m_top.Reset();
603f702b 12819 if (attr.m_bottom.IsValid())
24777478
JS
12820 m_bottom.Reset();
12821
12822 return true;
12823}
12824
12825// Collects the attributes that are common to a range of content, building up a note of
12826// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12827void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
12828{
12829 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12830 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12831 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12832 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12833}
12834
603f702b 12835// Partial equality test
32423dd8 12836bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
603f702b 12837{
32423dd8 12838 if (!m_width.EqPartial(size.m_width, weakTest))
603f702b
JS
12839 return false;
12840
32423dd8 12841 if (!m_height.EqPartial(size.m_height, weakTest))
603f702b
JS
12842 return false;
12843
12844 return true;
12845}
12846
12847// Apply border to 'this', but not if the same as compareWith
12848bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12849{
12850 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12851 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12852
12853 return true;
12854}
12855
12856// Remove specified attributes from this object
12857bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12858{
12859 if (attr.m_width.IsValid())
12860 m_width.Reset();
12861 if (attr.m_height.IsValid())
12862 m_height.Reset();
12863
12864 return true;
12865}
12866
12867// Collects the attributes that are common to a range of content, building up a note of
12868// which attributes are absent in some objects and which clash in some objects.
12869void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12870{
12871 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12872 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12873}
12874
24777478
JS
12875// Collects the attributes that are common to a range of content, building up a note of
12876// which attributes are absent in some objects and which clash in some objects.
12877void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12878{
12879 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12880 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12881
12882 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12883
c4168888
JS
12884 // If different font size units are being used, this is a clash.
12885 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
24777478 12886 {
c4168888
JS
12887 currentStyle.SetFontSize(0);
12888 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12889 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12890 }
12891 else
12892 {
12893 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
340ef5c5 12894 {
c4168888 12895 if (currentStyle.HasFontPointSize())
24777478 12896 {
c4168888 12897 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478 12898 {
c4168888
JS
12899 // Clash of attr - mark as such
12900 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12901 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
12902 }
12903 }
c4168888
JS
12904 else
12905 currentStyle.SetFontSize(attr.GetFontSize());
12906 }
12907 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
12908 {
12909 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12910 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
32423dd8
JS
12911 }
12912
c4168888 12913 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
24777478 12914 {
c4168888 12915 if (currentStyle.HasFontPixelSize())
24777478 12916 {
c4168888 12917 if (currentStyle.GetFontSize() != attr.GetFontSize())
24777478
JS
12918 {
12919 // Clash of attr - mark as such
c4168888
JS
12920 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12921 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
24777478
JS
12922 }
12923 }
12924 else
c4168888 12925 currentStyle.SetFontPixelSize(attr.GetFontSize());
24777478 12926 }
c4168888
JS
12927 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
12928 {
12929 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12930 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12931 }
12932 }
24777478 12933
c4168888
JS
12934 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12935 {
12936 if (currentStyle.HasFontItalic())
24777478 12937 {
c4168888 12938 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
24777478 12939 {
c4168888
JS
12940 // Clash of attr - mark as such
12941 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12942 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
24777478 12943 }
24777478 12944 }
c4168888
JS
12945 else
12946 currentStyle.SetFontStyle(attr.GetFontStyle());
12947 }
12948 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
12949 {
12950 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12951 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12952 }
24777478 12953
c4168888
JS
12954 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12955 {
12956 if (currentStyle.HasFontFamily())
24777478 12957 {
c4168888 12958 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
24777478 12959 {
c4168888
JS
12960 // Clash of attr - mark as such
12961 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12962 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
24777478 12963 }
24777478 12964 }
c4168888
JS
12965 else
12966 currentStyle.SetFontFamily(attr.GetFontFamily());
12967 }
12968 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
12969 {
12970 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12971 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12972 }
24777478 12973
c4168888
JS
12974 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12975 {
12976 if (currentStyle.HasFontWeight())
24777478 12977 {
c4168888 12978 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
24777478 12979 {
c4168888
JS
12980 // Clash of attr - mark as such
12981 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12982 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12983 }
12984 }
12985 else
12986 currentStyle.SetFontWeight(attr.GetFontWeight());
12987 }
12988 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
12989 {
12990 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12991 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12992 }
24777478 12993
c4168888
JS
12994 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12995 {
12996 if (currentStyle.HasFontFaceName())
12997 {
12998 wxString faceName1(currentStyle.GetFontFaceName());
12999 wxString faceName2(attr.GetFontFaceName());
13000
13001 if (faceName1 != faceName2)
13002 {
13003 // Clash of attr - mark as such
13004 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13005 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
24777478 13006 }
24777478 13007 }
c4168888
JS
13008 else
13009 currentStyle.SetFontFaceName(attr.GetFontFaceName());
13010 }
13011 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
13012 {
13013 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13014 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13015 }
24777478 13016
c4168888
JS
13017 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
13018 {
13019 if (currentStyle.HasFontUnderlined())
24777478 13020 {
c4168888 13021 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
24777478 13022 {
c4168888
JS
13023 // Clash of attr - mark as such
13024 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13025 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
24777478 13026 }
24777478 13027 }
c4168888
JS
13028 else
13029 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
13030 }
13031 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
13032 {
13033 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13034 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13035 }
32423dd8 13036
c4168888
JS
13037 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
13038 {
13039 if (currentStyle.HasFontStrikethrough())
32423dd8 13040 {
c4168888 13041 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
32423dd8 13042 {
c4168888
JS
13043 // Clash of attr - mark as such
13044 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13045 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
32423dd8 13046 }
32423dd8 13047 }
c4168888
JS
13048 else
13049 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13050 }
13051 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13052 {
13053 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13054 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
24777478
JS
13055 }
13056
13057 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13058 {
13059 if (currentStyle.HasTextColour())
13060 {
13061 if (currentStyle.GetTextColour() != attr.GetTextColour())
13062 {
13063 // Clash of attr - mark as such
13064 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13065 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13066 }
13067 }
13068 else
13069 currentStyle.SetTextColour(attr.GetTextColour());
13070 }
c4168888
JS
13071 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13072 {
13073 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13074 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13075 }
24777478
JS
13076
13077 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13078 {
13079 if (currentStyle.HasBackgroundColour())
13080 {
13081 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13082 {
13083 // Clash of attr - mark as such
13084 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13085 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13086 }
13087 }
13088 else
13089 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13090 }
c4168888
JS
13091 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13092 {
13093 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13094 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13095 }
24777478
JS
13096
13097 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13098 {
13099 if (currentStyle.HasAlignment())
13100 {
13101 if (currentStyle.GetAlignment() != attr.GetAlignment())
13102 {
13103 // Clash of attr - mark as such
13104 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13105 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13106 }
13107 }
13108 else
13109 currentStyle.SetAlignment(attr.GetAlignment());
13110 }
c4168888
JS
13111 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13112 {
13113 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13114 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13115 }
24777478
JS
13116
13117 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13118 {
13119 if (currentStyle.HasTabs())
13120 {
13121 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13122 {
13123 // Clash of attr - mark as such
13124 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13125 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13126 }
13127 }
13128 else
13129 currentStyle.SetTabs(attr.GetTabs());
13130 }
c4168888
JS
13131 else if (!attr.HasTabs() && currentStyle.HasTabs())
13132 {
13133 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13134 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13135 }
24777478
JS
13136
13137 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13138 {
13139 if (currentStyle.HasLeftIndent())
13140 {
13141 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13142 {
13143 // Clash of attr - mark as such
13144 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13145 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13146 }
13147 }
13148 else
13149 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13150 }
c4168888
JS
13151 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13152 {
13153 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13154 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13155 }
24777478
JS
13156
13157 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13158 {
13159 if (currentStyle.HasRightIndent())
13160 {
13161 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13162 {
13163 // Clash of attr - mark as such
13164 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13165 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13166 }
13167 }
13168 else
13169 currentStyle.SetRightIndent(attr.GetRightIndent());
13170 }
c4168888
JS
13171 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13172 {
13173 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13174 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13175 }
24777478
JS
13176
13177 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13178 {
13179 if (currentStyle.HasParagraphSpacingAfter())
13180 {
13181 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13182 {
13183 // Clash of attr - mark as such
13184 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13185 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13186 }
13187 }
13188 else
13189 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13190 }
c4168888
JS
13191 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13192 {
13193 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13194 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13195 }
24777478
JS
13196
13197 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13198 {
13199 if (currentStyle.HasParagraphSpacingBefore())
13200 {
13201 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13202 {
13203 // Clash of attr - mark as such
13204 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13205 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13206 }
13207 }
13208 else
13209 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13210 }
c4168888
JS
13211 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13212 {
13213 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13214 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13215 }
24777478
JS
13216
13217 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13218 {
13219 if (currentStyle.HasLineSpacing())
13220 {
13221 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13222 {
13223 // Clash of attr - mark as such
13224 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13225 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13226 }
13227 }
13228 else
13229 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13230 }
c4168888
JS
13231 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13232 {
13233 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13234 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13235 }
24777478
JS
13236
13237 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13238 {
13239 if (currentStyle.HasCharacterStyleName())
13240 {
13241 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13242 {
13243 // Clash of attr - mark as such
13244 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13245 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13246 }
13247 }
13248 else
13249 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13250 }
c4168888
JS
13251 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13252 {
13253 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13254 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13255 }
24777478
JS
13256
13257 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13258 {
13259 if (currentStyle.HasParagraphStyleName())
13260 {
13261 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13262 {
13263 // Clash of attr - mark as such
13264 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13265 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13266 }
13267 }
13268 else
13269 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13270 }
c4168888
JS
13271 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13272 {
13273 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13274 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13275 }
24777478
JS
13276
13277 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13278 {
13279 if (currentStyle.HasListStyleName())
13280 {
13281 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13282 {
13283 // Clash of attr - mark as such
13284 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13285 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13286 }
13287 }
13288 else
13289 currentStyle.SetListStyleName(attr.GetListStyleName());
13290 }
c4168888
JS
13291 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13292 {
13293 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13294 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13295 }
24777478
JS
13296
13297 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13298 {
13299 if (currentStyle.HasBulletStyle())
13300 {
13301 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13302 {
13303 // Clash of attr - mark as such
13304 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13305 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13306 }
13307 }
13308 else
13309 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13310 }
c4168888
JS
13311 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13312 {
13313 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13314 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13315 }
24777478
JS
13316
13317 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13318 {
13319 if (currentStyle.HasBulletNumber())
13320 {
13321 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13322 {
13323 // Clash of attr - mark as such
13324 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13325 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13326 }
13327 }
13328 else
13329 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13330 }
c4168888
JS
13331 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13332 {
13333 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13334 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13335 }
24777478
JS
13336
13337 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13338 {
13339 if (currentStyle.HasBulletText())
13340 {
13341 if (currentStyle.GetBulletText() != attr.GetBulletText())
13342 {
13343 // Clash of attr - mark as such
13344 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13345 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13346 }
13347 }
13348 else
13349 {
13350 currentStyle.SetBulletText(attr.GetBulletText());
13351 currentStyle.SetBulletFont(attr.GetBulletFont());
13352 }
13353 }
c4168888
JS
13354 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13355 {
13356 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13357 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13358 }
24777478
JS
13359
13360 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13361 {
13362 if (currentStyle.HasBulletName())
13363 {
13364 if (currentStyle.GetBulletName() != attr.GetBulletName())
13365 {
13366 // Clash of attr - mark as such
13367 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13368 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13369 }
13370 }
13371 else
13372 {
13373 currentStyle.SetBulletName(attr.GetBulletName());
13374 }
13375 }
c4168888
JS
13376 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13377 {
13378 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13379 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13380 }
24777478
JS
13381
13382 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13383 {
13384 if (currentStyle.HasURL())
13385 {
13386 if (currentStyle.GetURL() != attr.GetURL())
13387 {
13388 // Clash of attr - mark as such
13389 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13390 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13391 }
13392 }
13393 else
13394 {
13395 currentStyle.SetURL(attr.GetURL());
13396 }
13397 }
c4168888
JS
13398 else if (!attr.HasURL() && currentStyle.HasURL())
13399 {
13400 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13401 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13402 }
24777478
JS
13403
13404 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13405 {
13406 if (currentStyle.HasTextEffects())
13407 {
13408 // We need to find the bits in the new attr that are different:
13409 // just look at those bits that are specified by the new attr.
13410
13411 // We need to remove the bits and flags that are not common between current attr
13412 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13413 // previous styles.
13414
13415 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13416 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13417
13418 if (currentRelevantTextEffects != newRelevantTextEffects)
13419 {
13420 // Find the text effects that were different, using XOR
13421 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13422
13423 // Clash of attr - mark as such
13424 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13425 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13426 }
13427 }
13428 else
13429 {
13430 currentStyle.SetTextEffects(attr.GetTextEffects());
13431 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13432 }
13433
13434 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13435 // that we've looked at so far
13436 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13437 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13438
13439 if (currentStyle.GetTextEffectFlags() == 0)
13440 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13441 }
c4168888
JS
13442 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13443 {
13444 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13445 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13446 }
24777478
JS
13447
13448 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13449 {
13450 if (currentStyle.HasOutlineLevel())
13451 {
13452 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13453 {
13454 // Clash of attr - mark as such
13455 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13456 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13457 }
13458 }
13459 else
13460 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13461 }
c4168888
JS
13462 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13463 {
13464 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13465 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13466 }
24777478
JS
13467}
13468
bec80f4f
JS
13469WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13470
f7667b84
JS
13471// JACS 2013-01-27
13472WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13473
bec80f4f
JS
13474IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13475
13476bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13477{
13478 if (m_properties.GetCount() != props.GetCount())
13479 return false;
13480
13481 size_t i;
13482 for (i = 0; i < m_properties.GetCount(); i++)
13483 {
13484 const wxVariant& var1 = m_properties[i];
13485 int idx = props.Find(var1.GetName());
13486 if (idx == -1)
13487 return false;
13488 const wxVariant& var2 = props.m_properties[idx];
13489 if (!(var1 == var2))
13490 return false;
13491 }
13492
13493 return true;
13494}
13495
13496wxArrayString wxRichTextProperties::GetPropertyNames() const
13497{
13498 wxArrayString arr;
13499 size_t i;
13500 for (i = 0; i < m_properties.GetCount(); i++)
13501 {
13502 arr.Add(m_properties[i].GetName());
13503 }
13504 return arr;
13505}
13506
13507int wxRichTextProperties::Find(const wxString& name) const
13508{
13509 size_t i;
13510 for (i = 0; i < m_properties.GetCount(); i++)
13511 {
13512 if (m_properties[i].GetName() == name)
13513 return (int) i;
13514 }
13515 return -1;
13516}
13517
590a0f8b
JS
13518bool wxRichTextProperties::Remove(const wxString& name)
13519{
13520 int idx = Find(name);
13521 if (idx != -1)
13522 {
13523 m_properties.RemoveAt(idx);
13524 return true;
13525 }
13526 else
13527 return false;
13528}
13529
bec80f4f
JS
13530wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13531{
13532 int idx = Find(name);
13533 if (idx == wxNOT_FOUND)
13534 SetProperty(name, wxString());
13535 idx = Find(name);
13536 if (idx != wxNOT_FOUND)
13537 {
13538 return & (*this)[idx];
13539 }
13540 else
13541 return NULL;
13542}
13543
13544const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13545{
13546 static const wxVariant nullVariant;
13547 int idx = Find(name);
13548 if (idx != -1)
13549 return m_properties[idx];
13550 else
13551 return nullVariant;
13552}
13553
13554wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13555{
13556 return GetProperty(name).GetString();
13557}
13558
13559long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13560{
13561 return GetProperty(name).GetLong();
13562}
13563
13564bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13565{
13566 return GetProperty(name).GetBool();
13567}
13568
13569double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13570{
13571 return GetProperty(name).GetDouble();
13572}
13573
13574void wxRichTextProperties::SetProperty(const wxVariant& variant)
13575{
13576 wxASSERT(!variant.GetName().IsEmpty());
13577
13578 int idx = Find(variant.GetName());
13579
13580 if (idx == -1)
13581 m_properties.Add(variant);
13582 else
13583 m_properties[idx] = variant;
13584}
13585
13586void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13587{
13588 int idx = Find(name);
13589 wxVariant var(variant);
13590 var.SetName(name);
13591
13592 if (idx == -1)
13593 m_properties.Add(var);
13594 else
13595 m_properties[idx] = var;
13596}
13597
13598void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13599{
13600 SetProperty(name, wxVariant(value, name));
13601}
13602
13603void wxRichTextProperties::SetProperty(const wxString& name, long value)
13604{
13605 SetProperty(name, wxVariant(value, name));
13606}
13607
13608void wxRichTextProperties::SetProperty(const wxString& name, double value)
13609{
13610 SetProperty(name, wxVariant(value, name));
13611}
13612
13613void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13614{
13615 SetProperty(name, wxVariant(value, name));
13616}
24777478 13617
590a0f8b
JS
13618void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13619{
13620 size_t i;
13621 for (i = 0; i < properties.GetCount(); i++)
13622 {
13623 wxString name = properties.GetProperties()[i].GetName();
13624 if (HasProperty(name))
13625 Remove(name);
13626 }
13627}
13628
13629void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13630{
13631 size_t i;
13632 for (i = 0; i < properties.GetCount(); i++)
13633 {
13634 SetProperty(properties.GetProperties()[i]);
13635 }
13636}
13637
603f702b
JS
13638wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13639{
13640 if (m_address.GetCount() == 0)
13641 return topLevelContainer;
13642
13643 wxRichTextCompositeObject* p = topLevelContainer;
13644 size_t i = 0;
13645 while (p && i < m_address.GetCount())
13646 {
13647 int pos = m_address[i];
13648 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13649 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13650 return NULL;
13651
13652 wxRichTextObject* p1 = p->GetChild(pos);
13653 if (i == (m_address.GetCount()-1))
13654 return p1;
13655
13656 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13657 i ++;
13658 }
13659 return NULL;
13660}
13661
13662bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13663{
13664 m_address.Clear();
13665
13666 if (topLevelContainer == obj)
13667 return true;
13668
13669 wxRichTextObject* o = obj;
13670 while (o)
13671 {
13672 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13673 if (!p)
13674 return false;
13675
13676 int pos = p->GetChildren().IndexOf(o);
13677 if (pos == -1)
13678 return false;
13679
13680 m_address.Insert(pos, 0);
13681
13682 if (p == topLevelContainer)
13683 return true;
13684
13685 o = p;
13686 }
13687 return false;
13688}
13689
13690// Equality test
13691bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13692{
13693 if (m_container != sel.m_container)
13694 return false;
13695 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13696 return false;
13697 size_t i;
13698 for (i = 0; i < m_ranges.GetCount(); i++)
13699 if (!(m_ranges[i] == sel.m_ranges[i]))
13700 return false;
13701 return true;
13702}
13703
13704// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13705// or none at the level of the object's container.
13706wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13707{
13708 if (IsValid())
13709 {
13710 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13711
13712 if (container == m_container)
13713 return m_ranges;
13714
13715 container = obj->GetContainer();
13716 while (container)
13717 {
13718 if (container->GetParent())
13719 {
13720 // If we found that our object's container is within the range of
13721 // a selection higher up, then assume the whole original object
13722 // is also selected.
13723 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13724 if (parentContainer == m_container)
13725 {
13726 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13727 {
13728 wxRichTextRangeArray ranges;
13729 ranges.Add(obj->GetRange());
13730 return ranges;
13731 }
13732 }
13733
13734 container = parentContainer;
13735 }
13736 else
13737 {
13738 container = NULL;
13739 break;
13740 }
13741 }
13742 }
13743 return wxRichTextRangeArray();
13744}
13745
13746// Is the given position within the selection?
13747bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13748{
13749 if (!IsValid())
13750 return false;
13751 else
13752 {
13753 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13754 return WithinSelection(pos, selectionRanges);
13755 }
13756}
13757
13758// Is the given position within the selection range?
13759bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13760{
13761 size_t i;
13762 for (i = 0; i < ranges.GetCount(); i++)
13763 {
13764 const wxRichTextRange& range = ranges[i];
13765 if (pos >= range.GetStart() && pos <= range.GetEnd())
13766 return true;
13767 }
13768 return false;
13769}
13770
13771// Is the given range completely within the selection range?
13772bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13773{
13774 size_t i;
13775 for (i = 0; i < ranges.GetCount(); i++)
13776 {
13777 const wxRichTextRange& eachRange = ranges[i];
13778 if (range.IsWithin(eachRange))
13779 return true;
13780 }
13781 return false;
13782}
13783
8db2e3ef
JS
13784IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13785IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13786
f7667b84
JS
13787wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
13788{
13789 Init();
13790 m_buffer = buffer;
13791 if (m_buffer && m_buffer->GetRichTextCtrl())
13792 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13793}
13794
8db2e3ef
JS
13795bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13796{
f7667b84
JS
13797 if (!GetVirtualAttributesEnabled())
13798 return false;
13799
8db2e3ef
JS
13800 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13801 while (node)
13802 {
13803 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13804 if (handler->HasVirtualAttributes(obj))
13805 return true;
13806
13807 node = node->GetNext();
13808 }
13809 return false;
13810}
13811
13812wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13813{
13814 wxRichTextAttr attr;
f7667b84
JS
13815 if (!GetVirtualAttributesEnabled())
13816 return attr;
13817
8db2e3ef
JS
13818 // We apply all handlers, so we can may combine several different attributes
13819 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13820 while (node)
13821 {
13822 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13823 if (handler->HasVirtualAttributes(obj))
13824 {
13825 bool success = handler->GetVirtualAttributes(attr, obj);
13826 wxASSERT(success);
aa8f57f4 13827 wxUnusedVar(success);
8db2e3ef
JS
13828 }
13829
13830 node = node->GetNext();
13831 }
13832 return attr;
13833}
13834
13835bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13836{
f7667b84
JS
13837 if (!GetVirtualAttributesEnabled())
13838 return false;
13839
8db2e3ef
JS
13840 if (HasVirtualAttributes(obj))
13841 {
13842 wxRichTextAttr a(GetVirtualAttributes(obj));
13843 attr.Apply(a);
13844 return true;
13845 }
13846 else
13847 return false;
13848}
13849
f7667b84
JS
13850int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
13851{
13852 if (!GetVirtualAttributesEnabled())
13853 return 0;
13854
13855 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13856 while (node)
13857 {
13858 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13859 int count = handler->GetVirtualSubobjectAttributesCount(obj);
13860 if (count > 0)
13861 return count;
13862
13863 node = node->GetNext();
13864 }
13865 return 0;
13866}
13867
13868int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
13869{
13870 if (!GetVirtualAttributesEnabled())
13871 return 0;
13872
13873 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13874 while (node)
13875 {
13876 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13877 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
13878 return positions.GetCount();
13879
13880 node = node->GetNext();
13881 }
13882 return 0;
13883}
13884
13885bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
13886{
13887 if (!GetVirtualAttributesEnabled())
13888 return false;
13889
13890 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13891 while (node)
13892 {
13893 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13894 if (handler->HasVirtualText(obj))
13895 return true;
13896
13897 node = node->GetNext();
13898 }
13899 return false;
13900}
13901
13902bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
13903{
13904 if (!GetVirtualAttributesEnabled())
13905 return false;
13906
13907 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13908 while (node)
13909 {
13910 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13911 if (handler->GetVirtualText(obj, text))
13912 return true;
13913
13914 node = node->GetNext();
13915 }
13916 return false;
13917}
13918
8db2e3ef
JS
13919/// Adds a handler to the end
13920void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13921{
13922 sm_drawingHandlers.Append(handler);
13923}
13924
13925/// Inserts a handler at the front
13926void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13927{
13928 sm_drawingHandlers.Insert( handler );
13929}
13930
13931/// Removes a handler
13932bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13933{
13934 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13935 if (handler)
13936 {
13937 sm_drawingHandlers.DeleteObject(handler);
13938 delete handler;
13939 return true;
13940 }
13941 else
13942 return false;
13943}
13944
13945wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13946{
13947 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13948 while (node)
13949 {
13950 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13951 if (handler->GetName().Lower() == name.Lower()) return handler;
13952
13953 node = node->GetNext();
13954 }
13955 return NULL;
13956}
13957
13958void wxRichTextBuffer::CleanUpDrawingHandlers()
13959{
13960 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13961 while (node)
13962 {
13963 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13964 wxList::compatibility_iterator next = node->GetNext();
13965 delete handler;
13966 node = next;
13967 }
13968
13969 sm_drawingHandlers.Clear();
13970}
603f702b 13971
7c9fdebe
JS
13972void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13973{
13974 sm_fieldTypes[fieldType->GetName()] = fieldType;
13975}
13976
13977bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13978{
13979 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13980 if (it == sm_fieldTypes.end())
13981 return false;
13982 else
13983 {
13984 wxRichTextFieldType* fieldType = it->second;
13985 sm_fieldTypes.erase(it);
13986 delete fieldType;
13987 return true;
13988 }
13989}
13990
13991wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13992{
13993 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13994 if (it == sm_fieldTypes.end())
13995 return NULL;
13996 else
13997 return it->second;
13998}
13999
14000void wxRichTextBuffer::CleanUpFieldTypes()
14001{
14002 wxRichTextFieldTypeHashMap::iterator it;
14003 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
14004 {
14005 wxRichTextFieldType* fieldType = it->second;
14006 delete fieldType;
14007 }
14008
14009 sm_fieldTypes.clear();
14010}
14011
5d7836c4
JS
14012#endif
14013 // wxUSE_RICHTEXT