]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
Fix compilation in !wxUSE_LOG_DEBUG && !HAVE_VARIADIC_MACROS case.
[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{
cdaed652 468 // JACS: did I do this some time ago when testing? Should we re-enable it?
5cb0b827 469#if 0
ecb5fbf1
JS
470 const wxFont& font1 = dc.GetFont();
471 if (font1.IsOk() && font.IsOk())
472 {
473 if (font1.GetPointSize() == font.GetPointSize() &&
474 font1.GetFamily() == font.GetFamily() &&
475 font1.GetStyle() == font.GetStyle() &&
476 font1.GetWeight() == font.GetWeight() &&
3c8766f7 477 font1.GetUnderlined() == font.GetUnderlined() &&
9c4cb611 478 font1.GetFamily() == font.GetFamily() &&
ecb5fbf1
JS
479 font1.GetFaceName() == font.GetFaceName())
480 return;
481 }
5cb0b827 482#endif
ecb5fbf1
JS
483 dc.SetFont(font);
484}
485
486inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
487{
488 const wxPen& pen1 = dc.GetPen();
489 if (pen1.IsOk() && pen.IsOk())
490 {
491 if (pen1.GetWidth() == pen.GetWidth() &&
492 pen1.GetStyle() == pen.GetStyle() &&
493 pen1.GetColour() == pen.GetColour())
494 return;
495 }
496 dc.SetPen(pen);
497}
498
499inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
500{
501 const wxBrush& brush1 = dc.GetBrush();
502 if (brush1.IsOk() && brush.IsOk())
503 {
504 if (brush1.GetStyle() == brush.GetStyle() &&
505 brush1.GetColour() == brush.GetColour())
506 return;
507 }
508 dc.SetBrush(brush);
509}
510
5d7836c4
JS
511/*!
512 * wxRichTextObject
513 * This is the base for drawable objects.
514 */
515
516IMPLEMENT_CLASS(wxRichTextObject, wxObject)
517
518wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
519{
5d7836c4
JS
520 m_refCount = 1;
521 m_parent = parent;
5d7836c4 522 m_descent = 0;
603f702b 523 m_show = true;
5d7836c4
JS
524}
525
526wxRichTextObject::~wxRichTextObject()
527{
528}
529
530void wxRichTextObject::Dereference()
531{
532 m_refCount --;
533 if (m_refCount <= 0)
534 delete this;
535}
536
537/// Copy
538void wxRichTextObject::Copy(const wxRichTextObject& obj)
539{
540 m_size = obj.m_size;
603f702b
JS
541 m_maxSize = obj.m_maxSize;
542 m_minSize = obj.m_minSize;
5d7836c4 543 m_pos = obj.m_pos;
5d7836c4 544 m_range = obj.m_range;
603f702b 545 m_ownRange = obj.m_ownRange;
5d7836c4 546 m_attributes = obj.m_attributes;
bec80f4f 547 m_properties = obj.m_properties;
5d7836c4 548 m_descent = obj.m_descent;
603f702b
JS
549 m_show = obj.m_show;
550}
551
552// Get/set the top-level container of this object.
553wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
554{
555 const wxRichTextObject* p = this;
556 while (p)
557 {
558 if (p->IsTopLevel())
559 {
560 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
561 }
562 p = p->GetParent();
563 }
564 return NULL;
5d7836c4
JS
565}
566
567void wxRichTextObject::SetMargins(int margin)
568{
603f702b 569 SetMargins(margin, margin, margin, margin);
5d7836c4
JS
570}
571
572void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
573{
603f702b
JS
574 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
575 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
576 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
577 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
578}
579
580int wxRichTextObject::GetLeftMargin() const
581{
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
583}
584
585int wxRichTextObject::GetRightMargin() const
586{
587 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
588}
589
590int wxRichTextObject::GetTopMargin() const
591{
592 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
593}
594
595int wxRichTextObject::GetBottomMargin() const
596{
597 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
598}
599
600// Calculate the available content space in the given rectangle, given the
601// margins, border and padding specified in the object's attributes.
8db2e3ef 602wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
603f702b
JS
603{
604 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
605 marginRect = outerRect;
8db2e3ef
JS
606 wxRichTextAttr attr(GetAttributes());
607 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
608 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
609 return contentRect;
610}
611
612// Invalidate the buffer. With no argument, invalidates whole buffer.
613void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
614{
615 if (invalidRange != wxRICHTEXT_NONE)
616 {
23698b12
JS
617 // If this is a floating object, size may not be recalculated
618 // after floats have been collected in an early stage of Layout.
619 // So avoid resetting the cache for floating objects during layout.
620 if (!IsFloating())
621 SetCachedSize(wxDefaultSize);
603f702b
JS
622 SetMaxSize(wxDefaultSize);
623 SetMinSize(wxDefaultSize);
624 }
5d7836c4
JS
625}
626
44219ff0 627// Convert units in tenths of a millimetre to device units
cdaed652 628int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
5d7836c4 629{
44219ff0 630 // Unscale
bec80f4f
JS
631 double scale = 1.0;
632 if (GetBuffer())
633 scale = GetBuffer()->GetScale();
634 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
635
44219ff0
JS
636 return p;
637}
638
639// Convert units in tenths of a millimetre to device units
bec80f4f 640int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
44219ff0 641{
5d7836c4
JS
642 // There are ppi pixels in 254.1 "1/10 mm"
643
644 double pixels = ((double) units * (double)ppi) / 254.1;
bec80f4f
JS
645 if (scale != 1.0)
646 pixels /= scale;
5d7836c4 647
603f702b
JS
648 // If the result is very small, make it at least one pixel in size.
649 if (pixels == 0 && units > 0)
650 pixels = 1;
651
5d7836c4
JS
652 return (int) pixels;
653}
654
24777478
JS
655// Convert units in pixels to tenths of a millimetre
656int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
657{
658 int p = pixels;
bec80f4f
JS
659 double scale = 1.0;
660 if (GetBuffer())
661 scale = GetBuffer()->GetScale();
662
663 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
24777478
JS
664}
665
bec80f4f 666int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
24777478
JS
667{
668 // There are ppi pixels in 254.1 "1/10 mm"
bec80f4f
JS
669
670 double p = double(pixels);
671
672 if (scale != 1.0)
673 p *= scale;
674
675 int units = int( p * 254.1 / (double) ppi );
24777478
JS
676 return units;
677}
678
bec80f4f 679// Draw the borders and background for the given rectangle and attributes.
603f702b
JS
680// Width and height are taken to be the outer margin size, not the content.
681bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
bec80f4f
JS
682{
683 // Assume boxRect is the area around the content
603f702b
JS
684 wxRect marginRect = boxRect;
685 wxRect contentRect, borderRect, paddingRect, outlineRect;
bec80f4f 686
603f702b 687 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
bec80f4f
JS
688
689 // Margin is transparent. Draw background from margin.
603f702b 690 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
bec80f4f 691 {
603f702b
JS
692 wxColour colour;
693 if (flags & wxRICHTEXT_DRAW_SELECTED)
694 {
695 // TODO: get selection colour from control?
696 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
697 }
698 else
699 colour = attr.GetBackgroundColour();
700
701 wxPen pen(colour);
702 wxBrush brush(colour);
bec80f4f
JS
703
704 dc.SetPen(pen);
705 dc.SetBrush(brush);
37e7b783 706 dc.DrawRectangle(borderRect);
bec80f4f
JS
707 }
708
603f702b
JS
709 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
710 {
711 wxRichTextAttr editBorderAttr = attr;
712 // TODO: make guideline colour configurable
713 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
714 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
715 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
716
717 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
718 }
719
720 if (attr.GetTextBoxAttr().GetBorder().IsValid())
721 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
bec80f4f 722
603f702b
JS
723 if (attr.GetTextBoxAttr().GetOutline().IsValid())
724 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
bec80f4f
JS
725
726 return true;
727}
728
729// Draw a border
603f702b 730bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
bec80f4f
JS
731{
732 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
603f702b 733 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 734
603f702b 735 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
736 {
737 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
738 wxColour col(attr.GetLeft().GetColour());
739
740 // If pen width is > 1, resorts to a solid rectangle.
741 if (borderLeft == 1)
742 {
743 int penStyle = wxSOLID;
744 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
745 penStyle = wxDOT;
746 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
747 penStyle = wxLONG_DASH;
603f702b 748 wxPen pen(col, 1, penStyle);
bec80f4f
JS
749 dc.SetPen(pen);
750 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
751
752 }
753 else if (borderLeft > 1)
754 {
755 wxPen pen(col);
756 wxBrush brush(col);
757 dc.SetPen(pen);
758 dc.SetBrush(brush);
759 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
760 }
761 }
762
603f702b 763 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
764 {
765 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
766
767 wxColour col(attr.GetRight().GetColour());
768
769 // If pen width is > 1, resorts to a solid rectangle.
770 if (borderRight == 1)
771 {
772 int penStyle = wxSOLID;
773 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
774 penStyle = wxDOT;
775 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
776 penStyle = wxLONG_DASH;
603f702b 777 wxPen pen(col, 1, penStyle);
bec80f4f 778 dc.SetPen(pen);
603f702b 779 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
bec80f4f
JS
780
781 }
782 else if (borderRight > 1)
783 {
784 wxPen pen(col);
785 wxBrush brush(col);
786 dc.SetPen(pen);
787 dc.SetBrush(brush);
63af79de 788 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
bec80f4f
JS
789 }
790 }
791
603f702b 792 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
793 {
794 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
795
796 wxColour col(attr.GetTop().GetColour());
797
798 // If pen width is > 1, resorts to a solid rectangle.
799 if (borderTop == 1)
800 {
801 int penStyle = wxSOLID;
802 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
803 penStyle = wxDOT;
804 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
805 penStyle = wxLONG_DASH;
603f702b 806 wxPen pen(col, 1, penStyle);
bec80f4f
JS
807 dc.SetPen(pen);
808 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
809
810 }
811 else if (borderTop > 1)
812 {
813 wxPen pen(col);
814 wxBrush brush(col);
815 dc.SetPen(pen);
816 dc.SetBrush(brush);
817 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
818 }
819 }
820
603f702b 821 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
822 {
823 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
824 wxColour col(attr.GetTop().GetColour());
825
826 // If pen width is > 1, resorts to a solid rectangle.
827 if (borderBottom == 1)
828 {
829 int penStyle = wxSOLID;
830 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
831 penStyle = wxDOT;
832 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
833 penStyle = wxLONG_DASH;
603f702b 834 wxPen pen(col, 1, penStyle);
bec80f4f
JS
835 dc.SetPen(pen);
836 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
837
838 }
839 else if (borderBottom > 1)
840 {
841 wxPen pen(col);
842 wxBrush brush(col);
843 dc.SetPen(pen);
844 dc.SetBrush(brush);
63af79de 845 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
bec80f4f
JS
846 }
847 }
848
849 return true;
850}
851
852// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
853// or marginRect (outer), and the other must be the default rectangle (no width or height).
854// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
855// is available.
856//
857// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
858
603f702b 859bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
bec80f4f
JS
860{
861 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
862 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
863 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
864 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
865
603f702b 866 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 867
603f702b 868 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f 869 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
603f702b 870 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
bec80f4f 871 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
603f702b 872 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
bec80f4f 873 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
83c6ae8e 874 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
bec80f4f
JS
875 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
876
603f702b 877 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f 878 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
603f702b 879 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
bec80f4f 880 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
603f702b 881 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
bec80f4f 882 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
83c6ae8e 883 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
bec80f4f
JS
884 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
885
603f702b 886 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
bec80f4f 887 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
603f702b 888 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
bec80f4f 889 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
603f702b 890 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
bec80f4f 891 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b 892 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
bec80f4f
JS
893 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
894
603f702b 895 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
bec80f4f 896 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
603f702b 897 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
bec80f4f 898 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
603f702b 899 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
bec80f4f 900 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
603f702b 901 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
bec80f4f
JS
902 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
903
904 int leftTotal = marginLeft + borderLeft + paddingLeft;
905 int rightTotal = marginRight + borderRight + paddingRight;
906 int topTotal = marginTop + borderTop + paddingTop;
907 int bottomTotal = marginBottom + borderBottom + paddingBottom;
908
909 if (marginRect != wxRect())
910 {
911 contentRect.x = marginRect.x + leftTotal;
912 contentRect.y = marginRect.y + topTotal;
913 contentRect.width = marginRect.width - (leftTotal + rightTotal);
914 contentRect.height = marginRect.height - (topTotal + bottomTotal);
915 }
916 else
917 {
918 marginRect.x = contentRect.x - leftTotal;
919 marginRect.y = contentRect.y - topTotal;
920 marginRect.width = contentRect.width + (leftTotal + rightTotal);
921 marginRect.height = contentRect.height + (topTotal + bottomTotal);
922 }
923
924 borderRect.x = marginRect.x + marginLeft;
925 borderRect.y = marginRect.y + marginTop;
926 borderRect.width = marginRect.width - (marginLeft + marginRight);
927 borderRect.height = marginRect.height - (marginTop + marginBottom);
928
929 paddingRect.x = marginRect.x + marginLeft + borderLeft;
930 paddingRect.y = marginRect.y + marginTop + borderTop;
931 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
932 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
933
934 // The outline is outside the margin and doesn't influence the overall box position or content size.
935 outlineRect.x = marginRect.x - outlineLeft;
936 outlineRect.y = marginRect.y - outlineTop;
937 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
938 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
939
940 return true;
941}
942
603f702b
JS
943// Get the total margin for the object in pixels, taking into account margin, padding and border size
944bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
945 int& topMargin, int& bottomMargin)
946{
947 // Assume boxRect is the area around the content
948 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
949 marginRect = wxRect(0, 0, 1000, 1000);
bec80f4f 950
603f702b
JS
951 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
952
953 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
954 rightMargin = marginRect.GetRight() - contentRect.GetRight();
955 topMargin = contentRect.GetTop() - marginRect.GetTop();
956 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
957
958 return true;
959}
960
961// Returns the rectangle which the child has available to it given restrictions specified in the
962// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
bb7bbd12
JS
963// availableContainerSpace might be a parent that the cell has to compute its width relative to.
964// E.g. a cell that's 50% of its parent.
965wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
603f702b
JS
966{
967 wxRect rect = availableParentSpace;
968 double scale = 1.0;
969 if (buffer)
970 scale = buffer->GetScale();
971
bb7bbd12 972 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
603f702b
JS
973
974 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
975 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
976
977 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
978 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
979
980 // Can specify either left or right for the position (we're assuming we can't
981 // set the left and right edges to effectively set the size. Would we want to do that?)
982 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
983 {
984 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
985 }
986 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
987 {
988 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
989 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 990 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
603f702b
JS
991 else
992 rect.x += x;
993 }
994
995 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
996 {
997 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
998 }
999 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
1000 {
1001 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
1002 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 1003 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
603f702b
JS
1004 else
1005 rect.y += y;
1006 }
1007
bb7bbd12
JS
1008 if (rect.GetWidth() > availableParentSpace.GetWidth())
1009 rect.SetWidth(availableParentSpace.GetWidth());
1010
603f702b
JS
1011 return rect;
1012}
1013
1014// Dump to output stream for debugging
5d7836c4
JS
1015void wxRichTextObject::Dump(wxTextOutputStream& stream)
1016{
1017 stream << GetClassInfo()->GetClassName() << wxT("\n");
1018 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");
1019 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");
1020}
1021
603f702b 1022// Gets the containing buffer
44219ff0
JS
1023wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1024{
1025 const wxRichTextObject* obj = this;
1026 while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer)))
1027 obj = obj->GetParent();
1028 return wxDynamicCast(obj, wxRichTextBuffer);
1029}
5d7836c4 1030
603f702b
JS
1031// Get the absolute object position, by traversing up the child/parent hierarchy
1032wxPoint wxRichTextObject::GetAbsolutePosition() const
1033{
1034 wxPoint pt = GetPosition();
1035
1036 wxRichTextObject* p = GetParent();
1037 while (p)
1038 {
1039 pt = pt + p->GetPosition();
1040 p = p->GetParent();
1041 }
1042
1043 return pt;
1044}
1045
1046// Hit-testing: returns a flag indicating hit test details, plus
1047// information about position
8db2e3ef 1048int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
603f702b
JS
1049{
1050 if (!IsShown())
1051 return wxRICHTEXT_HITTEST_NONE;
1052
1053 wxRect rect = GetRect();
1054 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1055 pt.y >= rect.y && pt.y < rect.y + rect.height)
1056 {
1057 *obj = this;
1058 *contextObj = GetParentContainer();
1059 textPosition = GetRange().GetStart();
1060 return wxRICHTEXT_HITTEST_ON;
1061 }
1062 else
1063 return wxRICHTEXT_HITTEST_NONE;
1064}
1065
1066// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1067// lays out the object again using the maximum ('best') size
8db2e3ef 1068bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
bb7bbd12
JS
1069 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1070 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
603f702b
JS
1071 int style)
1072{
bb7bbd12 1073 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
603f702b 1074 wxRect originalAvailableRect = availableChildRect;
8db2e3ef 1075 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1076
1077 wxSize maxSize = GetMaxSize();
1078
1079 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1080 // on this basis
bb7bbd12 1081 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
603f702b
JS
1082 {
1083 // Redo the layout with a fixed, minimum size this time.
1084 Invalidate(wxRICHTEXT_ALL);
1085 wxRichTextAttr newAttr(attr);
1086 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1087 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1088
bb7bbd12 1089 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
603f702b
JS
1090
1091 // If a paragraph, align the whole paragraph.
1092 // Problem with this: if we're limited by a floating object, a line may be centered
1093 // w.r.t. the smaller resulting box rather than the actual available width.
07d4142f 1094 if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
603f702b
JS
1095 {
1096 // centering, right-justification
8db2e3ef 1097 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
1098 {
1099 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1100 }
8db2e3ef 1101 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
1102 {
1103 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1104 }
1105 }
1106
8db2e3ef 1107 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1108 }
1109
1110 /*
1111 __________________
1112 | ____________ |
1113 | | | |
1114
1115
1116 */
1117
1118 return true;
1119}
1120
1121// Move the object recursively, by adding the offset from old to new
1122void wxRichTextObject::Move(const wxPoint& pt)
1123{
1124 SetPosition(pt);
1125}
1126
1127
5d7836c4
JS
1128/*!
1129 * wxRichTextCompositeObject
1130 * This is the base for drawable objects.
1131 */
1132
1133IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1134
1135wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1136 wxRichTextObject(parent)
1137{
1138}
1139
1140wxRichTextCompositeObject::~wxRichTextCompositeObject()
1141{
1142 DeleteChildren();
1143}
1144
1145/// Get the nth child
1146wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1147{
1148 wxASSERT ( n < m_children.GetCount() );
1149
1150 return m_children.Item(n)->GetData();
1151}
1152
1153/// Append a child, returning the position
1154size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1155{
1156 m_children.Append(child);
1157 child->SetParent(this);
1158 return m_children.GetCount() - 1;
1159}
1160
1161/// Insert the child in front of the given object, or at the beginning
1162bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1163{
1164 if (inFrontOf)
1165 {
1166 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1167 m_children.Insert(node, child);
1168 }
1169 else
1170 m_children.Insert(child);
1171 child->SetParent(this);
1172
1173 return true;
1174}
1175
1176/// Delete the child
1177bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1178{
1179 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1180 if (node)
1181 {
efbf6735
JS
1182 wxRichTextObject* obj = node->GetData();
1183 m_children.Erase(node);
5d7836c4 1184 if (deleteChild)
efbf6735 1185 delete obj;
5d7836c4
JS
1186
1187 return true;
1188 }
1189 return false;
1190}
1191
1192/// Delete all children
1193bool wxRichTextCompositeObject::DeleteChildren()
1194{
1195 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1196 while (node)
1197 {
1198 wxRichTextObjectList::compatibility_iterator oldNode = node;
1199
1200 wxRichTextObject* child = node->GetData();
1201 child->Dereference(); // Only delete if reference count is zero
1202
1203 node = node->GetNext();
efbf6735 1204 m_children.Erase(oldNode);
5d7836c4
JS
1205 }
1206
1207 return true;
1208}
1209
1210/// Get the child count
1211size_t wxRichTextCompositeObject::GetChildCount() const
1212{
1213 return m_children.GetCount();
1214}
1215
1216/// Copy
1217void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1218{
1219 wxRichTextObject::Copy(obj);
1220
1221 DeleteChildren();
1222
1223 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1224 while (node)
1225 {
1226 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
1227 wxRichTextObject* newChild = child->Clone();
1228 newChild->SetParent(this);
1229 m_children.Append(newChild);
5d7836c4
JS
1230
1231 node = node->GetNext();
1232 }
1233}
1234
1235/// Hit-testing: returns a flag indicating hit test details, plus
1236/// information about position
8db2e3ef 1237int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 1238{
603f702b
JS
1239 if (!IsShown())
1240 return wxRICHTEXT_HITTEST_NONE;
1241
5d7836c4
JS
1242 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1243 while (node)
1244 {
1245 wxRichTextObject* child = node->GetData();
1246
603f702b
JS
1247 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1248 {
1249 // Just check if we hit the overall object
8db2e3ef 1250 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1251 if (ret != wxRICHTEXT_HITTEST_NONE)
1252 return ret;
1253 }
1254 else if (child->IsShown())
1255 {
8db2e3ef 1256 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1257 if (ret != wxRICHTEXT_HITTEST_NONE)
1258 return ret;
1259 }
5d7836c4
JS
1260
1261 node = node->GetNext();
1262 }
1263
603f702b 1264 return wxRICHTEXT_HITTEST_NONE;
5d7836c4
JS
1265}
1266
1267/// Finds the absolute position and row height for the given character position
8db2e3ef 1268bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
1269{
1270 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1271 while (node)
1272 {
1273 wxRichTextObject* child = node->GetData();
1274
603f702b
JS
1275 // Don't recurse if the child is a top-level object,
1276 // such as a text box, because the character position will no longer
1277 // apply. By definition, a top-level object has its own range of
1278 // character positions.
8db2e3ef 1279 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
5d7836c4
JS
1280 return true;
1281
1282 node = node->GetNext();
1283 }
1284
1285 return false;
1286}
1287
1288/// Calculate range
1289void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1290{
1291 long current = start;
1292 long lastEnd = current;
1293
603f702b
JS
1294 if (IsTopLevel())
1295 {
1296 current = 0;
1297 lastEnd = 0;
1298 }
1299
5d7836c4
JS
1300 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1301 while (node)
1302 {
1303 wxRichTextObject* child = node->GetData();
1304 long childEnd = 0;
1305
1306 child->CalculateRange(current, childEnd);
1307 lastEnd = childEnd;
1308
1309 current = childEnd + 1;
1310
1311 node = node->GetNext();
1312 }
1313
603f702b
JS
1314 if (IsTopLevel())
1315 {
1316 // A top-level object always has a range of size 1,
1317 // because its children don't count at this level.
1318 end = start;
1319 m_range.SetRange(start, start);
5d7836c4 1320
603f702b
JS
1321 // An object with no children has zero length
1322 if (m_children.GetCount() == 0)
1323 lastEnd --;
1324 m_ownRange.SetRange(0, lastEnd);
1325 }
1326 else
1327 {
1328 end = lastEnd;
5d7836c4 1329
603f702b
JS
1330 // An object with no children has zero length
1331 if (m_children.GetCount() == 0)
1332 end --;
1333
1334 m_range.SetRange(start, end);
1335 }
5d7836c4
JS
1336}
1337
1338/// Delete range from layout.
1339bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1340{
1341 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1342
5d7836c4
JS
1343 while (node)
1344 {
1345 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1346 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1347
5d7836c4
JS
1348 // Delete the range in each paragraph
1349
1350 // When a chunk has been deleted, internally the content does not
1351 // now match the ranges.
1352 // However, so long as deletion is not done on the same object twice this is OK.
1353 // If you may delete content from the same object twice, recalculate
1354 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1355 // adjust the range you're deleting accordingly.
7fe8059f 1356
5d7836c4
JS
1357 if (!obj->GetRange().IsOutside(range))
1358 {
603f702b
JS
1359 // No need to delete within a top-level object; just removing this object will do fine
1360 if (!obj->IsTopLevel())
1361 obj->DeleteRange(range);
5d7836c4
JS
1362
1363 // Delete an empty object, or paragraph within this range.
1364 if (obj->IsEmpty() ||
1365 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1366 {
1367 // An empty paragraph has length 1, so won't be deleted unless the
1368 // whole range is deleted.
7fe8059f 1369 RemoveChild(obj, true);
5d7836c4
JS
1370 }
1371 }
7fe8059f 1372
5d7836c4
JS
1373 node = next;
1374 }
7fe8059f 1375
5d7836c4
JS
1376 return true;
1377}
1378
1379/// Get any text in this object for the given range
1380wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1381{
1382 wxString text;
1383 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1384 while (node)
1385 {
1386 wxRichTextObject* child = node->GetData();
1387 wxRichTextRange childRange = range;
1388 if (!child->GetRange().IsOutside(range))
1389 {
1390 childRange.LimitTo(child->GetRange());
7fe8059f 1391
5d7836c4 1392 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1393
5d7836c4
JS
1394 text += childText;
1395 }
1396 node = node->GetNext();
1397 }
1398
1399 return text;
1400}
1401
603f702b
JS
1402/// Get the child object at the given character position
1403wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1404{
1405 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1406 while (node)
1407 {
1408 wxRichTextObject* child = node->GetData();
1409 if (child->GetRange().GetStart() == pos)
1410 return child;
1411 node = node->GetNext();
1412 }
1413 return NULL;
1414}
1415
5d7836c4 1416/// Recursively merge all pieces that can be merged.
109bfc88 1417bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
5d7836c4
JS
1418{
1419 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1420 while (node)
1421 {
1422 wxRichTextObject* child = node->GetData();
5cb0b827 1423 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
5d7836c4 1424 {
109bfc88
JS
1425 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1426 if (composite)
1427 composite->Defragment();
1428
1429 if (node->GetNext())
5d7836c4 1430 {
109bfc88
JS
1431 wxRichTextObject* nextChild = node->GetNext()->GetData();
1432 if (child->CanMerge(nextChild) && child->Merge(nextChild))
1433 {
1434 nextChild->Dereference();
1435 m_children.Erase(node->GetNext());
5d7836c4 1436
109bfc88
JS
1437 // Don't set node -- we'll see if we can merge again with the next
1438 // child.
1439 }
1440 else
1441 node = node->GetNext();
5d7836c4
JS
1442 }
1443 else
1444 node = node->GetNext();
1445 }
1446 else
1447 node = node->GetNext();
1448 }
1449
bec80f4f
JS
1450 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1451 if (GetChildCount() > 1)
5d7836c4 1452 {
bec80f4f
JS
1453 node = m_children.GetFirst();
1454 while (node)
1455 {
1456 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1457 wxRichTextObject* child = node->GetData();
1458 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1459 {
1460 if (child->IsEmpty())
1461 {
1462 child->Dereference();
1463 m_children.Erase(node);
1464 }
1465 node = next;
1466 }
1467 else
1468 node = node->GetNext();
1469 }
5d7836c4 1470 }
5d7836c4 1471
5d7836c4
JS
1472 return true;
1473}
1474
bec80f4f
JS
1475/// Dump to output stream for debugging
1476void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1477{
1478 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1479 while (node)
1480 {
1481 wxRichTextObject* child = node->GetData();
bec80f4f 1482 child->Dump(stream);
5d7836c4
JS
1483 node = node->GetNext();
1484 }
5d7836c4
JS
1485}
1486
603f702b
JS
1487/// Get/set the object size for the given range. Returns false if the range
1488/// is invalid for this object.
8db2e3ef 1489bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b
JS
1490{
1491 if (!range.IsWithin(GetRange()))
1492 return false;
5d7836c4 1493
603f702b 1494 wxSize sz;
5d7836c4 1495
603f702b
JS
1496 wxArrayInt childExtents;
1497 wxArrayInt* p;
1498 if (partialExtents)
1499 p = & childExtents;
1500 else
1501 p = NULL;
5d7836c4 1502
603f702b
JS
1503 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1504 while (node)
cdaed652 1505 {
603f702b
JS
1506 wxRichTextObject* child = node->GetData();
1507 if (!child->GetRange().IsOutside(range))
1508 {
1509 // Floating objects have a zero size within the paragraph.
1510 if (child->IsFloating())
1511 {
1512 if (partialExtents)
1513 {
1514 int lastSize;
1515 if (partialExtents->GetCount() > 0)
1516 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1517 else
1518 lastSize = 0;
cdaed652 1519
603f702b
JS
1520 partialExtents->Add(0 /* zero size */ + lastSize);
1521 }
1522 }
1523 else
1524 {
1525 wxSize childSize;
5d7836c4 1526
603f702b
JS
1527 wxRichTextRange rangeToUse = range;
1528 rangeToUse.LimitTo(child->GetRange());
1529 if (child->IsTopLevel())
1530 rangeToUse = child->GetOwnRange();
5d7836c4 1531
603f702b 1532 int childDescent = 0;
cdaed652 1533
603f702b
JS
1534 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1535 // but it's only going to be used after caching has taken place.
1536 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1537 {
1538 childDescent = child->GetDescent();
1539 childSize = child->GetCachedSize();
bec80f4f 1540
603f702b
JS
1541 sz.y = wxMax(sz.y, childSize.y);
1542 sz.x += childSize.x;
1543 descent = wxMax(descent, childDescent);
1544 }
8db2e3ef 1545 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b
JS
1546 {
1547 sz.y = wxMax(sz.y, childSize.y);
1548 sz.x += childSize.x;
1549 descent = wxMax(descent, childDescent);
bec80f4f 1550
603f702b
JS
1551 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1552 {
1553 child->SetCachedSize(childSize);
1554 child->SetDescent(childDescent);
1555 }
bec80f4f 1556
603f702b
JS
1557 if (partialExtents)
1558 {
1559 int lastSize;
1560 if (partialExtents->GetCount() > 0)
1561 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1562 else
1563 lastSize = 0;
bec80f4f 1564
603f702b
JS
1565 size_t i;
1566 for (i = 0; i < childExtents.GetCount(); i++)
1567 {
1568 partialExtents->Add(childExtents[i] + lastSize);
1569 }
1570 }
1571 }
1572 }
1573
1574 if (p)
1575 p->Clear();
1576 }
1577
1578 node = node->GetNext();
1579 }
1580 size = sz;
1581 return true;
1582}
1583
1584// Invalidate the buffer. With no argument, invalidates whole buffer.
1585void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1586{
1587 wxRichTextObject::Invalidate(invalidRange);
1588
1589 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1590 while (node)
1591 {
1592 wxRichTextObject* child = node->GetData();
1593 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1594 {
1595 // Skip
1596 }
1597 else if (child->IsTopLevel())
1598 {
1599 if (invalidRange == wxRICHTEXT_NONE)
1600 child->Invalidate(wxRICHTEXT_NONE);
1601 else
1602 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1603 }
1604 else
1605 child->Invalidate(invalidRange);
1606 node = node->GetNext();
1607 }
1608}
1609
1610// Move the object recursively, by adding the offset from old to new
1611void wxRichTextCompositeObject::Move(const wxPoint& pt)
1612{
1613 wxPoint oldPos = GetPosition();
1614 SetPosition(pt);
1615 wxPoint offset = pt - oldPos;
1616
1617 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1618 while (node)
1619 {
1620 wxRichTextObject* child = node->GetData();
1621 wxPoint childPos = child->GetPosition() + offset;
1622 child->Move(childPos);
1623 node = node->GetNext();
1624 }
1625}
1626
1627
1628/*!
1629 * wxRichTextParagraphLayoutBox
1630 * This box knows how to lay out paragraphs.
1631 */
1632
1633IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1634
1635wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1636 wxRichTextCompositeObject(parent)
1637{
1638 Init();
1639}
1640
1641wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1642{
1643 if (m_floatCollector)
1644 {
1645 delete m_floatCollector;
1646 m_floatCollector = NULL;
1647 }
1648}
1649
1650/// Initialize the object.
1651void wxRichTextParagraphLayoutBox::Init()
1652{
1653 m_ctrl = NULL;
1654
1655 // For now, assume is the only box and has no initial size.
1656 m_range = wxRichTextRange(0, -1);
1657 m_ownRange = wxRichTextRange(0, -1);
1658
1659 m_invalidRange = wxRICHTEXT_ALL;
1660
603f702b
JS
1661 m_partialParagraph = false;
1662 m_floatCollector = NULL;
1663}
1664
1665void wxRichTextParagraphLayoutBox::Clear()
1666{
1667 DeleteChildren();
1668
1669 if (m_floatCollector)
1670 delete m_floatCollector;
1671 m_floatCollector = NULL;
1672 m_partialParagraph = false;
1673}
1674
1675/// Copy
1676void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1677{
1678 Clear();
1679
1680 wxRichTextCompositeObject::Copy(obj);
1681
1682 m_partialParagraph = obj.m_partialParagraph;
1683 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1684}
1685
07d4142f
JS
1686// Gather information about floating objects; only gather floats for those paragraphs that
1687// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1688// during layout.
603f702b 1689bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1690{
1691 if (m_floatCollector != NULL)
1692 delete m_floatCollector;
603f702b 1693 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652 1694 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
07d4142f
JS
1695 // Only gather floats up to the point we'll start formatting paragraphs.
1696 while (untilObj && node && node->GetData() != untilObj)
cdaed652
VZ
1697 {
1698 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1699 wxASSERT (child != NULL);
1700 if (child)
1701 m_floatCollector->CollectFloat(child);
1702 node = node->GetNext();
1703 }
ce00f59b 1704
cdaed652
VZ
1705 return true;
1706}
1707
603f702b
JS
1708// Returns the style sheet associated with the overall buffer.
1709wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1710{
1711 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1712}
1713
1714// Get the number of floating objects at this level
1715int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1716{
1717 if (m_floatCollector)
1718 return m_floatCollector->GetFloatingObjectCount();
1719 else
1720 return 0;
1721}
1722
1723// Get a list of floating objects
1724bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1725{
1726 if (m_floatCollector)
1727 {
1728 return m_floatCollector->GetFloatingObjects(objects);
1729 }
1730 else
1731 return false;
1732}
1733
1734// Calculate ranges
1735void wxRichTextParagraphLayoutBox::UpdateRanges()
1736{
1737 long start = 0;
1738 if (GetParent())
1739 start = GetRange().GetStart();
1740 long end;
1741 CalculateRange(start, end);
1742}
1743
cdaed652 1744// HitTest
8db2e3ef 1745int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1746{
603f702b
JS
1747 if (!IsShown())
1748 return wxRICHTEXT_HITTEST_NONE;
1749
cdaed652 1750 int ret = wxRICHTEXT_HITTEST_NONE;
343ef639 1751 if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
8db2e3ef 1752 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
ce00f59b 1753
cdaed652 1754 if (ret == wxRICHTEXT_HITTEST_NONE)
8db2e3ef 1755 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
cdaed652 1756 else
603f702b
JS
1757 {
1758 *contextObj = this;
cdaed652 1759 return ret;
603f702b 1760 }
cdaed652
VZ
1761}
1762
1763/// Draw the floating objects
8db2e3ef 1764void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
1765{
1766 if (m_floatCollector)
8db2e3ef 1767 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
cdaed652
VZ
1768}
1769
bec80f4f 1770void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1771{
1772 if (from == to)
1773 return;
1774
1775 from->RemoveChild(obj);
1776 to->AppendChild(obj);
5d7836c4
JS
1777}
1778
1779/// Draw the item
8db2e3ef 1780bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1781{
603f702b
JS
1782 if (!IsShown())
1783 return true;
1784
1785 wxRect thisRect(GetPosition(), GetCachedSize());
1786
8db2e3ef
JS
1787 wxRichTextAttr attr(GetAttributes());
1788 context.ApplyVirtualAttributes(attr, this);
1789
603f702b
JS
1790 int flags = style;
1791 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1792 flags |= wxRICHTEXT_DRAW_SELECTED;
1793
1794 // Don't draw guidelines if at top level
1795 int theseFlags = flags;
1796 if (!GetParent())
1797 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
8db2e3ef 1798 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
603f702b 1799
8db2e3ef 1800 DrawFloats(dc, context, range, selection, rect, descent, style);
5d7836c4
JS
1801 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1802 while (node)
1803 {
603f702b 1804 wxRichTextObject* child = node->GetData();
7fe8059f 1805
5d7836c4
JS
1806 if (child && !child->GetRange().IsOutside(range))
1807 {
1808 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1809 wxRichTextRange childRange = range;
1810 if (child->IsTopLevel())
1811 {
1812 childRange = child->GetOwnRange();
1813 }
7fe8059f 1814
ea160b2e
JS
1815 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1816 {
1817 // Stop drawing
1818 break;
1819 }
1820 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1821 {
1822 // Skip
1823 }
1824 else
8db2e3ef 1825 child->Draw(dc, context, childRange, selection, rect, descent, style);
5d7836c4
JS
1826 }
1827
1828 node = node->GetNext();
1829 }
1830 return true;
1831}
1832
1833/// Lay the item out
8db2e3ef 1834bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 1835{
603f702b
JS
1836 SetPosition(rect.GetPosition());
1837
1838 if (!IsShown())
1839 return true;
1840
4d551ad5
JS
1841 wxRect availableSpace;
1842 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1843
8db2e3ef
JS
1844 wxRichTextAttr attr(GetAttributes());
1845 context.ApplyVirtualAttributes(attr, this);
1846
4d551ad5 1847 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1848 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1849 // so that during a size, only the visible part will be relaid out, or
1850 // it would take too long causing flicker. As an approximation, we assume that
1851 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1852 if (formatRect)
1853 {
603f702b 1854 wxRect rect2(0, 0, rect.width, rect.height);
8db2e3ef 1855 availableSpace = GetAvailableContentArea(dc, context, rect2);
4d551ad5
JS
1856
1857 // Invalidate the part of the buffer from the first visible line
1858 // to the end. If other parts of the buffer are currently invalid,
1859 // then they too will be taken into account if they are above
1860 // the visible point.
1861 long startPos = 0;
1862 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1863 if (line)
1864 startPos = line->GetAbsoluteRange().GetStart();
1865
603f702b 1866 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1867 }
1868 else
603f702b 1869 {
8db2e3ef 1870 availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
1871 }
1872
d157d142
JS
1873 // Fix the width if we're at the top level
1874 if (!GetParent())
1875 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1876
603f702b 1877 int leftMargin, rightMargin, topMargin, bottomMargin;
8db2e3ef 1878 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
603f702b 1879 topMargin, bottomMargin);
5d7836c4
JS
1880
1881 int maxWidth = 0;
603f702b
JS
1882 int maxHeight = 0;
1883
1884 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1885 int maxMaxWidth = 0;
1886
1887 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1888 int maxMinWidth = 0;
1889
1890 // If we have vertical alignment, we must recalculate everything.
8db2e3ef
JS
1891 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1892 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1893
5d7836c4 1894 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1895
38113684 1896 bool layoutAll = true;
1e967276 1897
38113684
JS
1898 // Get invalid range, rounding to paragraph start/end.
1899 wxRichTextRange invalidRange = GetInvalidRange(true);
1900
4d551ad5 1901 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1902 return true;
1903
603f702b 1904 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1905 layoutAll = true;
38113684 1906 else // If we know what range is affected, start laying out from that point on.
603f702b 1907 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1908 {
38113684 1909 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1910 if (firstParagraph)
1911 {
1912 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1913 wxRichTextObjectList::compatibility_iterator previousNode;
1914 if ( firstNode )
1915 previousNode = firstNode->GetPrevious();
9b4af7b7 1916 if (firstNode)
2c375f42 1917 {
9b4af7b7
JS
1918 if (previousNode)
1919 {
1920 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1921 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1922 }
7fe8059f 1923
2c375f42
JS
1924 // Now we're going to start iterating from the first affected paragraph.
1925 node = firstNode;
1e967276
JS
1926
1927 layoutAll = false;
2c375f42
JS
1928 }
1929 }
1930 }
1931
07d4142f
JS
1932 // Gather information about only those floating objects that will not be formatted,
1933 // after which floats will be gathered per-paragraph during layout.
603f702b 1934 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1935
4d551ad5
JS
1936 // A way to force speedy rest-of-buffer layout (the 'else' below)
1937 bool forceQuickLayout = false;
39a1c2f2 1938
d3f6b1b5
JS
1939 // First get the size of the paragraphs we won't be laying out
1940 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1941 while (n && n != node)
1942 {
1943 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
1944 if (child)
1945 {
1946 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1947 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1948 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1949 }
1950 n = n->GetNext();
1951 }
1952
5d7836c4
JS
1953 while (node)
1954 {
1955 // Assume this box only contains paragraphs
1956
1957 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
706465df
JS
1958 // Unsure if this is needed
1959 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 1960
603f702b 1961 if (child && child->IsShown())
2c375f42 1962 {
603f702b
JS
1963 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1964 if ( !forceQuickLayout &&
1965 (layoutAll ||
1966 child->GetLines().IsEmpty() ||
1967 !child->GetRange().IsOutside(invalidRange)) )
1968 {
1969 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1970 // lays out the object again using the minimum size
8db2e3ef
JS
1971 child->LayoutToBestSize(dc, context, GetBuffer(),
1972 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
1973
1974 // Layout must set the cached size
1975 availableSpace.y += child->GetCachedSize().y;
1976 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1977 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1978 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1979
1980 // If we're just formatting the visible part of the buffer,
1981 // and we're now past the bottom of the window, and we don't have any
1982 // floating objects (since they may cause wrapping to change for the rest of the
1983 // the buffer), start quick layout.
1984 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
1985 forceQuickLayout = true;
1986 }
1987 else
1988 {
1989 // We're outside the immediately affected range, so now let's just
1990 // move everything up or down. This assumes that all the children have previously
1991 // been laid out and have wrapped line lists associated with them.
1992 // TODO: check all paragraphs before the affected range.
1993
1994 int inc = availableSpace.y - child->GetPosition().y;
1995
1996 while (node)
1997 {
1998 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1999 if (child)
2000 {
2001 if (child->GetLines().GetCount() == 0)
2002 {
2003 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2004 // lays out the object again using the minimum size
8db2e3ef
JS
2005 child->LayoutToBestSize(dc, context, GetBuffer(),
2006 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
2007
2008 //child->Layout(dc, availableChildRect, style);
2009 }
2010 else
2011 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 2012
603f702b
JS
2013 availableSpace.y += child->GetCachedSize().y;
2014 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2015 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2016 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2017 }
4d551ad5 2018
603f702b
JS
2019 node = node->GetNext();
2020 }
2021 break;
2022 }
2c375f42 2023 }
7fe8059f 2024
603f702b
JS
2025 node = node->GetNext();
2026 }
2027
2028 node = m_children.GetLast();
2029 if (node && node->GetData()->IsShown())
2030 {
2031 wxRichTextObject* child = node->GetData();
603f702b
JS
2032 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2033 }
2034 else
2035 maxHeight = 0; // topMargin + bottomMargin;
2036
23698b12
JS
2037 // Check the bottom edge of any floating object
2038 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2039 {
2040 int bottom = GetFloatCollector()->GetLastRectBottom();
2041 if (bottom > maxHeight)
2042 maxHeight = bottom;
2043 }
2044
8db2e3ef 2045 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
bb7bbd12 2046 {
8db2e3ef 2047 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
bb7bbd12
JS
2048 int w = r.GetWidth();
2049
2050 // Convert external to content rect
2051 w = w - leftMargin - rightMargin;
2052 maxWidth = wxMax(maxWidth, w);
2053 maxMaxWidth = wxMax(maxMaxWidth, w);
2054 }
2055
603f702b
JS
2056 // TODO: (also in para layout) should set the
2057 // object's size to an absolute one if specified,
2058 // but if not specified, calculate it from content.
2059
2060 // We need to add back the margins etc.
2061 {
2062 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2063 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
8db2e3ef 2064 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2065 SetCachedSize(marginRect.GetSize());
2066 }
2067
2068 // The maximum size is the greatest of all maximum widths for all paragraphs.
2069 {
2070 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2071 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2072 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2073 SetMaxSize(marginRect.GetSize());
2074 }
2075
2076 // The minimum size is the greatest of all minimum widths for all paragraphs.
2077 {
2078 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2079 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2080 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2081 SetMinSize(marginRect.GetSize());
2082 }
2083
8db2e3ef
JS
2084 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2085 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
603f702b
JS
2086 {
2087 int yOffset = 0;
2088 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2089 if (leftOverSpace > 0)
2090 {
8db2e3ef 2091 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
603f702b
JS
2092 {
2093 yOffset = (leftOverSpace/2);
2094 }
8db2e3ef 2095 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
603f702b
JS
2096 {
2097 yOffset = leftOverSpace;
2098 }
2099 }
7fe8059f 2100
603f702b
JS
2101 // Move all the children to vertically align the content
2102 // This doesn't take into account floating objects, unfortunately.
2103 if (yOffset != 0)
2104 {
2105 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
2106 while (node)
2107 {
2108 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2109 if (child)
603f702b 2110 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
2111
2112 node = node->GetNext();
2c375f42 2113 }
2c375f42 2114 }
5d7836c4
JS
2115 }
2116
1e967276 2117 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2118
2119 return true;
2120}
2121
5d7836c4 2122/// Get/set the size for the given range.
8db2e3ef 2123bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
5d7836c4
JS
2124{
2125 wxSize sz;
2126
09f14108
JS
2127 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2128 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2129
2130 // First find the first paragraph whose starting position is within the range.
2131 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2132 while (node)
2133 {
2134 // child is a paragraph
2135 wxRichTextObject* child = node->GetData();
2136 const wxRichTextRange& r = child->GetRange();
2137
2138 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2139 {
2140 startPara = node;
2141 break;
2142 }
2143
2144 node = node->GetNext();
2145 }
2146
2147 // Next find the last paragraph containing part of the range
2148 node = m_children.GetFirst();
2149 while (node)
2150 {
2151 // child is a paragraph
2152 wxRichTextObject* child = node->GetData();
2153 const wxRichTextRange& r = child->GetRange();
2154
2155 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2156 {
2157 endPara = node;
2158 break;
2159 }
2160
2161 node = node->GetNext();
2162 }
2163
2164 if (!startPara || !endPara)
2165 return false;
2166
2167 // Now we can add up the sizes
2168 for (node = startPara; node ; node = node->GetNext())
2169 {
2170 // child is a paragraph
2171 wxRichTextObject* child = node->GetData();
2172 const wxRichTextRange& childRange = child->GetRange();
2173 wxRichTextRange rangeToFind = range;
2174 rangeToFind.LimitTo(childRange);
2175
603f702b
JS
2176 if (child->IsTopLevel())
2177 rangeToFind = child->GetOwnRange();
2178
5d7836c4
JS
2179 wxSize childSize;
2180
2181 int childDescent = 0;
8db2e3ef 2182 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
5d7836c4
JS
2183
2184 descent = wxMax(childDescent, descent);
2185
2186 sz.x = wxMax(sz.x, childSize.x);
2187 sz.y += childSize.y;
2188
2189 if (node == endPara)
2190 break;
2191 }
2192
2193 size = sz;
2194
2195 return true;
2196}
2197
2198/// Get the paragraph at the given position
2199wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2200{
2201 if (caretPosition)
2202 pos ++;
2203
2204 // First find the first paragraph whose starting position is within the range.
2205 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2206 while (node)
2207 {
2208 // child is a paragraph
2209 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2210 // wxASSERT (child != NULL);
5d7836c4 2211
603f702b
JS
2212 if (child)
2213 {
2214 // Return first child in buffer if position is -1
2215 // if (pos == -1)
2216 // return child;
5d7836c4 2217
603f702b
JS
2218 if (child->GetRange().Contains(pos))
2219 return child;
2220 }
5d7836c4
JS
2221
2222 node = node->GetNext();
2223 }
2224 return NULL;
2225}
2226
2227/// Get the line at the given position
2228wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2229{
2230 if (caretPosition)
2231 pos ++;
2232
2233 // First find the first paragraph whose starting position is within the range.
2234 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2235 while (node)
2236 {
7051fa41
JS
2237 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2238 if (obj->GetRange().Contains(pos))
5d7836c4 2239 {
7051fa41
JS
2240 // child is a paragraph
2241 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2242 // wxASSERT (child != NULL);
7051fa41 2243
603f702b 2244 if (child)
7051fa41 2245 {
603f702b
JS
2246 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2247 while (node2)
2248 {
2249 wxRichTextLine* line = node2->GetData();
5d7836c4 2250
603f702b 2251 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2252
603f702b 2253 if (range.Contains(pos) ||
5d7836c4 2254
603f702b
JS
2255 // If the position is end-of-paragraph, then return the last line of
2256 // of the paragraph.
2257 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2258 return line;
5d7836c4 2259
603f702b
JS
2260 node2 = node2->GetNext();
2261 }
7051fa41 2262 }
7fe8059f 2263 }
5d7836c4
JS
2264
2265 node = node->GetNext();
2266 }
2267
2268 int lineCount = GetLineCount();
2269 if (lineCount > 0)
2270 return GetLineForVisibleLineNumber(lineCount-1);
2271 else
2272 return NULL;
2273}
2274
2275/// Get the line at the given y pixel position, or the last line.
2276wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2277{
2278 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2279 while (node)
2280 {
2281 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2282 // wxASSERT (child != NULL);
5d7836c4 2283
603f702b 2284 if (child)
5d7836c4 2285 {
603f702b
JS
2286 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2287 while (node2)
2288 {
2289 wxRichTextLine* line = node2->GetData();
5d7836c4 2290
603f702b 2291 wxRect rect(line->GetRect());
5d7836c4 2292
603f702b
JS
2293 if (y <= rect.GetBottom())
2294 return line;
5d7836c4 2295
603f702b
JS
2296 node2 = node2->GetNext();
2297 }
7fe8059f 2298 }
5d7836c4
JS
2299
2300 node = node->GetNext();
2301 }
2302
2303 // Return last line
2304 int lineCount = GetLineCount();
2305 if (lineCount > 0)
2306 return GetLineForVisibleLineNumber(lineCount-1);
2307 else
2308 return NULL;
2309}
2310
2311/// Get the number of visible lines
2312int wxRichTextParagraphLayoutBox::GetLineCount() const
2313{
2314 int count = 0;
2315
2316 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2317 while (node)
2318 {
2319 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2320 // wxASSERT (child != NULL);
2321
2322 if (child)
2323 count += child->GetLines().GetCount();
5d7836c4 2324
5d7836c4
JS
2325 node = node->GetNext();
2326 }
2327 return count;
2328}
2329
2330
2331/// Get the paragraph for a given line
2332wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2333{
1e967276 2334 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2335}
2336
2337/// Get the line size at the given position
2338wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2339{
2340 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2341 if (line)
2342 {
2343 return line->GetSize();
2344 }
2345 else
2346 return wxSize(0, 0);
2347}
2348
2349
2350/// Convenience function to add a paragraph of text
24777478 2351wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2352{
fe5aa22c 2353 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2354 // be combined at display time.
2355 // Divide into paragraph and character styles.
3e541562 2356
24777478
JS
2357 wxRichTextAttr defaultCharStyle;
2358 wxRichTextAttr defaultParaStyle;
4f32b3cf 2359
5607c890
JS
2360 // If the default style is a named paragraph style, don't apply any character formatting
2361 // to the initial text string.
2362 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2363 {
2364 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2365 if (def)
2366 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2367 }
2368 else
2369 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2370
24777478
JS
2371 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2372 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2373
2374 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
5d7836c4
JS
2375
2376 AppendChild(para);
2377
2378 UpdateRanges();
5d7836c4
JS
2379
2380 return para->GetRange();
2381}
2382
2383/// Adds multiple paragraphs, based on newlines.
24777478 2384wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2385{
fe5aa22c 2386 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2387 // be combined at display time.
2388 // Divide into paragraph and character styles.
3e541562 2389
24777478
JS
2390 wxRichTextAttr defaultCharStyle;
2391 wxRichTextAttr defaultParaStyle;
5607c890
JS
2392
2393 // If the default style is a named paragraph style, don't apply any character formatting
2394 // to the initial text string.
2395 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2396 {
2397 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2398 if (def)
2399 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2400 }
2401 else
2402 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2403
24777478
JS
2404 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2405 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2406
5d7836c4
JS
2407 wxRichTextParagraph* firstPara = NULL;
2408 wxRichTextParagraph* lastPara = NULL;
2409
2410 wxRichTextRange range(-1, -1);
0ca07313 2411
5d7836c4 2412 size_t i = 0;
28f92d74 2413 size_t len = text.length();
5d7836c4 2414 wxString line;
4f32b3cf 2415 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
0ca07313
JS
2416
2417 AppendChild(para);
2418
2419 firstPara = para;
2420 lastPara = para;
2421
5d7836c4
JS
2422 while (i < len)
2423 {
2424 wxChar ch = text[i];
2425 if (ch == wxT('\n') || ch == wxT('\r'))
2426 {
99404ab0
JS
2427 if (i != (len-1))
2428 {
2429 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2430 plainText->SetText(line);
0ca07313 2431
99404ab0 2432 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
5d7836c4 2433
99404ab0 2434 AppendChild(para);
0ca07313 2435
99404ab0
JS
2436 lastPara = para;
2437 line = wxEmptyString;
2438 }
5d7836c4
JS
2439 }
2440 else
2441 line += ch;
2442
2443 i ++;
2444 }
0ca07313 2445
7fe8059f 2446 if (!line.empty())
5d7836c4 2447 {
0ca07313
JS
2448 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2449 plainText->SetText(line);
5d7836c4
JS
2450 }
2451
5d7836c4 2452 UpdateRanges();
0ca07313 2453
0ca07313 2454 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2455}
2456
2457/// Convenience function to add an image
24777478 2458wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2459{
fe5aa22c 2460 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2461 // be combined at display time.
2462 // Divide into paragraph and character styles.
3e541562 2463
24777478
JS
2464 wxRichTextAttr defaultCharStyle;
2465 wxRichTextAttr defaultParaStyle;
5607c890
JS
2466
2467 // If the default style is a named paragraph style, don't apply any character formatting
2468 // to the initial text string.
2469 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2470 {
2471 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2472 if (def)
2473 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2474 }
2475 else
2476 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2477
24777478
JS
2478 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2479 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2480
4f32b3cf
JS
2481 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2482 AppendChild(para);
2483 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2484
5d7836c4 2485 UpdateRanges();
5d7836c4
JS
2486
2487 return para->GetRange();
2488}
2489
2490
2491/// Insert fragment into this box at the given position. If partialParagraph is true,
2492/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2493/// marker.
5d7836c4 2494
0ca07313 2495bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2496{
5d7836c4
JS
2497 // First, find the first paragraph whose starting position is within the range.
2498 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2499 if (para)
2500 {
24777478 2501 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2502
5d7836c4
JS
2503 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2504
2505 // Now split at this position, returning the object to insert the new
2506 // ones in front of.
2507 wxRichTextObject* nextObject = para->SplitAt(position);
2508
2509 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2510 // text, for example, so let's optimize.
2511
2512 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2513 {
2514 // Add the first para to this para...
2515 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2516 if (!firstParaNode)
2517 return false;
2518
2519 // Iterate through the fragment paragraph inserting the content into this paragraph.
2520 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2521 wxASSERT (firstPara != NULL);
2522
2523 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2524 while (objectNode)
2525 {
2526 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2527
5d7836c4
JS
2528 if (!nextObject)
2529 {
2530 // Append
2531 para->AppendChild(newObj);
2532 }
2533 else
2534 {
2535 // Insert before nextObject
2536 para->InsertChild(newObj, nextObject);
2537 }
7fe8059f 2538
5d7836c4
JS
2539 objectNode = objectNode->GetNext();
2540 }
2541
2542 return true;
2543 }
2544 else
2545 {
2546 // Procedure for inserting a fragment consisting of a number of
2547 // paragraphs:
2548 //
2549 // 1. Remove and save the content that's after the insertion point, for adding
2550 // back once we've added the fragment.
2551 // 2. Add the content from the first fragment paragraph to the current
2552 // paragraph.
2553 // 3. Add remaining fragment paragraphs after the current paragraph.
2554 // 4. Add back the saved content from the first paragraph. If partialParagraph
2555 // is true, add it to the last paragraph added and not a new one.
2556
2557 // 1. Remove and save objects after split point.
2558 wxList savedObjects;
2559 if (nextObject)
2560 para->MoveToList(nextObject, savedObjects);
2561
2562 // 2. Add the content from the 1st fragment paragraph.
2563 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2564 if (!firstParaNode)
2565 return false;
2566
2567 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2568 wxASSERT(firstPara != NULL);
2569
6c0ea513
JS
2570 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2571 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2572
2573 // Save empty paragraph attributes for appending later
2574 // These are character attributes deliberately set for a new paragraph. Without this,
2575 // we couldn't pass default attributes when appending a new paragraph.
24777478 2576 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2577
5d7836c4 2578 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2579
2580 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2581 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2582
5d7836c4
JS
2583 while (objectNode)
2584 {
c025e094 2585 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2586
c025e094
JS
2587 // Append
2588 para->AppendChild(newObj);
7fe8059f 2589
5d7836c4
JS
2590 objectNode = objectNode->GetNext();
2591 }
2592
2593 // 3. Add remaining fragment paragraphs after the current paragraph.
2594 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2595 wxRichTextObject* nextParagraph = NULL;
2596 if (nextParagraphNode)
2597 nextParagraph = nextParagraphNode->GetData();
2598
2599 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2600 wxRichTextParagraph* finalPara = para;
2601
99404ab0
JS
2602 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2603
5d7836c4 2604 // If there was only one paragraph, we need to insert a new one.
99404ab0 2605 while (i)
5d7836c4 2606 {
99404ab0
JS
2607 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2608 wxASSERT( para != NULL );
5d7836c4 2609
99404ab0 2610 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2611
2612 if (nextParagraph)
2613 InsertChild(finalPara, nextParagraph);
2614 else
7fe8059f 2615 AppendChild(finalPara);
99404ab0
JS
2616
2617 i = i->GetNext();
5d7836c4 2618 }
5d7836c4 2619
99404ab0
JS
2620 // If there was only one paragraph, or we have full paragraphs in our fragment,
2621 // we need to insert a new one.
2622 if (needExtraPara)
2623 {
2624 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2625
2626 if (nextParagraph)
2627 InsertChild(finalPara, nextParagraph);
2628 else
2629 AppendChild(finalPara);
5d7836c4
JS
2630 }
2631
2632 // 4. Add back the remaining content.
2633 if (finalPara)
2634 {
c025e094
JS
2635 if (nextObject)
2636 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2637
2638 // Ensure there's at least one object
2639 if (finalPara->GetChildCount() == 0)
2640 {
7fe8059f 2641 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2642 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2643
2644 finalPara->AppendChild(text);
2645 }
2646 }
2647
6c0ea513
JS
2648 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2649 finalPara->SetAttributes(firstPara->GetAttributes());
2650 else if (finalPara && finalPara != para)
99404ab0
JS
2651 finalPara->SetAttributes(originalAttr);
2652
5d7836c4
JS
2653 return true;
2654 }
2655 }
2656 else
2657 {
2658 // Append
2659 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2660 while (i)
2661 {
2662 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2663 wxASSERT( para != NULL );
7fe8059f 2664
5d7836c4 2665 AppendChild(para->Clone());
7fe8059f 2666
5d7836c4
JS
2667 i = i->GetNext();
2668 }
2669
2670 return true;
2671 }
5d7836c4
JS
2672}
2673
2674/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2675/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2676bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2677{
2678 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2679 while (i)
2680 {
2681 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2682 wxASSERT( para != NULL );
2683
2684 if (!para->GetRange().IsOutside(range))
2685 {
2686 fragment.AppendChild(para->Clone());
7fe8059f 2687 }
5d7836c4
JS
2688 i = i->GetNext();
2689 }
2690
2691 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2692 if (!fragment.IsEmpty())
2693 {
5d7836c4
JS
2694 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2695 wxASSERT( firstPara != NULL );
2696
0e190fa2
JS
2697 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2698 wxASSERT( lastPara != NULL );
2699
2700 if (!firstPara || !lastPara)
2701 return false;
2702
2703 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2704
2705 long firstPos = firstPara->GetRange().GetStart();
2706
2707 // Adjust for renumbering from zero
2708 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2709
2710 long end;
2711 fragment.CalculateRange(0, end);
2712
5d7836c4 2713 // Chop off the start of the paragraph
0e190fa2 2714 if (topTailRange.GetStart() > 0)
5d7836c4 2715 {
0e190fa2 2716 wxRichTextRange r(0, topTailRange.GetStart()-1);
5d7836c4
JS
2717 firstPara->DeleteRange(r);
2718
2719 // Make sure the numbering is correct
0e190fa2 2720 fragment.CalculateRange(0, end);
5d7836c4
JS
2721
2722 // Now, we've deleted some positions, so adjust the range
2723 // accordingly.
0e190fa2
JS
2724 topTailRange.SetStart(range.GetLength());
2725 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2726 }
2727 else
2728 {
2729 topTailRange.SetStart(range.GetLength());
2730 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
5d7836c4
JS
2731 }
2732
61e6149e 2733 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
5d7836c4 2734 {
0e190fa2 2735 lastPara->DeleteRange(topTailRange);
5d7836c4
JS
2736
2737 // Make sure the numbering is correct
2738 long end;
0e190fa2 2739 fragment.CalculateRange(0, end);
5d7836c4
JS
2740
2741 // We only have part of a paragraph at the end
2742 fragment.SetPartialParagraph(true);
2743 }
2744 else
2745 {
0e190fa2
JS
2746 // We have a partial paragraph (don't save last new paragraph marker)
2747 // or complete paragraph
2748 fragment.SetPartialParagraph(isFragment);
5d7836c4
JS
2749 }
2750 }
2751
2752 return true;
2753}
2754
2755/// Given a position, get the number of the visible line (potentially many to a paragraph),
2756/// starting from zero at the start of the buffer.
2757long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2758{
2759 if (caretPosition)
2760 pos ++;
2761
2762 int lineCount = 0;
2763
2764 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2765 while (node)
2766 {
2767 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2768 // wxASSERT( child != NULL );
5d7836c4 2769
603f702b 2770 if (child)
5d7836c4 2771 {
603f702b 2772 if (child->GetRange().Contains(pos))
5d7836c4 2773 {
603f702b
JS
2774 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2775 while (node2)
5d7836c4 2776 {
603f702b
JS
2777 wxRichTextLine* line = node2->GetData();
2778 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2779
603f702b
JS
2780 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2781 {
2782 // If the caret is displayed at the end of the previous wrapped line,
2783 // we want to return the line it's _displayed_ at (not the actual line
2784 // containing the position).
2785 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2786 return lineCount - 1;
2787 else
2788 return lineCount;
2789 }
7fe8059f 2790
603f702b
JS
2791 lineCount ++;
2792
2793 node2 = node2->GetNext();
2794 }
2795 // If we didn't find it in the lines, it must be
2796 // the last position of the paragraph. So return the last line.
2797 return lineCount-1;
5d7836c4 2798 }
603f702b
JS
2799 else
2800 lineCount += child->GetLines().GetCount();
5d7836c4 2801 }
5d7836c4
JS
2802
2803 node = node->GetNext();
2804 }
2805
2806 // Not found
2807 return -1;
2808}
2809
2810/// Given a line number, get the corresponding wxRichTextLine object.
2811wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2812{
2813 int lineCount = 0;
2814
2815 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2816 while (node)
2817 {
2818 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2819 // wxASSERT(child != NULL);
5d7836c4 2820
603f702b 2821 if (child)
5d7836c4 2822 {
603f702b 2823 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2824 {
603f702b
JS
2825 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2826 while (node2)
2827 {
2828 wxRichTextLine* line = node2->GetData();
7fe8059f 2829
603f702b
JS
2830 if (lineCount == lineNumber)
2831 return line;
5d7836c4 2832
603f702b 2833 lineCount ++;
7fe8059f 2834
603f702b
JS
2835 node2 = node2->GetNext();
2836 }
7fe8059f 2837 }
603f702b
JS
2838 else
2839 lineCount += child->GetLines().GetCount();
5d7836c4 2840 }
5d7836c4
JS
2841
2842 node = node->GetNext();
2843 }
2844
2845 // Didn't find it
2846 return NULL;
2847}
2848
2849/// Delete range from layout.
2850bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2851{
2852 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2853
99404ab0 2854 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2855 while (node)
2856 {
2857 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2858 // wxASSERT (obj != NULL);
5d7836c4
JS
2859
2860 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2861
603f702b 2862 if (obj)
5d7836c4 2863 {
603f702b 2864 // Delete the range in each paragraph
99404ab0 2865
603f702b 2866 if (!obj->GetRange().IsOutside(range))
5d7836c4 2867 {
603f702b
JS
2868 // Deletes the content of this object within the given range
2869 obj->DeleteRange(range);
99404ab0 2870
603f702b
JS
2871 wxRichTextRange thisRange = obj->GetRange();
2872 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2873
603f702b
JS
2874 // If the whole paragraph is within the range to delete,
2875 // delete the whole thing.
2876 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2877 {
603f702b
JS
2878 // Delete the whole object
2879 RemoveChild(obj, true);
2880 obj = NULL;
99404ab0 2881 }
603f702b
JS
2882 else if (!firstPara)
2883 firstPara = obj;
5d7836c4 2884
603f702b
JS
2885 // If the range includes the paragraph end, we need to join this
2886 // and the next paragraph.
2887 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2888 {
603f702b
JS
2889 // We need to move the objects from the next paragraph
2890 // to this paragraph
2891
2892 wxRichTextParagraph* nextParagraph = NULL;
2893 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2894 nextParagraph = obj;
6c0ea513 2895 else
603f702b
JS
2896 {
2897 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2898 if (next)
2899 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2900 }
5d7836c4 2901
603f702b
JS
2902 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2903
2904 wxRichTextAttr nextParaAttr;
2905 if (applyFinalParagraphStyle)
2906 {
2907 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2908 // not the next one.
2909 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2910 nextParaAttr = thisAttr;
2911 else
2912 nextParaAttr = nextParagraph->GetAttributes();
2913 }
5d7836c4 2914
603f702b 2915 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2916 {
603f702b
JS
2917 // Move the objects to the previous para
2918 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2919
603f702b
JS
2920 while (node1)
2921 {
2922 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2923
603f702b 2924 firstPara->AppendChild(obj1);
5d7836c4 2925
603f702b
JS
2926 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2927 nextParagraph->GetChildren().Erase(node1);
99404ab0 2928
603f702b
JS
2929 node1 = next1;
2930 }
5d7836c4 2931
603f702b
JS
2932 // Delete the paragraph
2933 RemoveChild(nextParagraph, true);
2934 }
fa01bfdd 2935
603f702b
JS
2936 // Avoid empty paragraphs
2937 if (firstPara && firstPara->GetChildren().GetCount() == 0)
2938 {
2939 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2940 firstPara->AppendChild(text);
2941 }
99404ab0 2942
603f702b
JS
2943 if (applyFinalParagraphStyle)
2944 firstPara->SetAttributes(nextParaAttr);
2945
2946 return true;
2947 }
5d7836c4
JS
2948 }
2949 }
7fe8059f 2950
5d7836c4
JS
2951 node = next;
2952 }
7fe8059f 2953
5d7836c4
JS
2954 return true;
2955}
2956
2957/// Get any text in this object for the given range
2958wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
2959{
2960 int lineCount = 0;
2961 wxString text;
2962 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2963 while (node)
2964 {
2965 wxRichTextObject* child = node->GetData();
2966 if (!child->GetRange().IsOutside(range))
2967 {
5d7836c4
JS
2968 wxRichTextRange childRange = range;
2969 childRange.LimitTo(child->GetRange());
7fe8059f 2970
5d7836c4 2971 wxString childText = child->GetTextForRange(childRange);
7fe8059f 2972
5d7836c4
JS
2973 text += childText;
2974
1a75935d 2975 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
2976 text += wxT("\n");
2977
5d7836c4
JS
2978 lineCount ++;
2979 }
2980 node = node->GetNext();
2981 }
2982
2983 return text;
2984}
2985
2986/// Get all the text
2987wxString wxRichTextParagraphLayoutBox::GetText() const
2988{
c99f1b0f 2989 return GetTextForRange(GetOwnRange());
5d7836c4
JS
2990}
2991
2992/// Get the paragraph by number
2993wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
2994{
27e20452 2995 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
2996 return NULL;
2997
2998 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
2999}
3000
3001/// Get the length of the paragraph
3002int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3003{
3004 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3005 if (para)
3006 return para->GetRange().GetLength() - 1; // don't include newline
3007 else
3008 return 0;
3009}
3010
3011/// Get the text of the paragraph
3012wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3013{
3014 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3015 if (para)
3016 return para->GetTextForRange(para->GetRange());
3017 else
3018 return wxEmptyString;
3019}
3020
3021/// Convert zero-based line column and paragraph number to a position.
3022long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3023{
3024 wxRichTextParagraph* para = GetParagraphAtLine(y);
3025 if (para)
3026 {
3027 return para->GetRange().GetStart() + x;
3028 }
3029 else
3030 return -1;
3031}
3032
3033/// Convert zero-based position to line column and paragraph number
3034bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3035{
3036 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3037 if (para)
3038 {
3039 int count = 0;
3040 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3041 while (node)
3042 {
3043 wxRichTextObject* child = node->GetData();
3044 if (child == para)
3045 break;
3046 count ++;
3047 node = node->GetNext();
3048 }
3049
3050 *y = count;
3051 *x = pos - para->GetRange().GetStart();
3052
3053 return true;
3054 }
3055 else
3056 return false;
3057}
3058
3059/// Get the leaf object in a paragraph at this position.
3060/// Given a line number, get the corresponding wxRichTextLine object.
3061wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3062{
3063 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3064 if (para)
3065 {
3066 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 3067
5d7836c4
JS
3068 while (node)
3069 {
3070 wxRichTextObject* child = node->GetData();
3071 if (child->GetRange().Contains(position))
3072 return child;
7fe8059f 3073
5d7836c4
JS
3074 node = node->GetNext();
3075 }
3076 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3077 return para->GetChildren().GetLast()->GetData();
3078 }
3079 return NULL;
3080}
3081
3082/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 3083bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
3084{
3085 bool characterStyle = false;
3086 bool paragraphStyle = false;
3087
3088 if (style.IsCharacterStyle())
3089 characterStyle = true;
3090 if (style.IsParagraphStyle())
3091 paragraphStyle = true;
3092
603f702b
JS
3093 wxRichTextBuffer* buffer = GetBuffer();
3094
59509217
JS
3095 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3096 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3097 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3098 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 3099 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 3100 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
3101
3102 // Apply paragraph style first, if any
24777478 3103 wxRichTextAttr wholeStyle(style);
523d2f14 3104
603f702b 3105 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 3106 {
603f702b 3107 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 3108 if (def)
603f702b 3109 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 3110 }
59509217
JS
3111
3112 // Limit the attributes to be set to the content to only character attributes.
24777478 3113 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
3114 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3115
603f702b 3116 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 3117 {
603f702b 3118 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 3119 if (def)
603f702b 3120 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
3121 }
3122
5d7836c4
JS
3123 // If we are associated with a control, make undoable; otherwise, apply immediately
3124 // to the data.
3125
603f702b 3126 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3127
3128 wxRichTextAction* action = NULL;
7fe8059f 3129
5d7836c4
JS
3130 if (haveControl && withUndo)
3131 {
603f702b 3132 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3133 action->SetRange(range);
603f702b 3134 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3135 }
3136
3137 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3138 while (node)
3139 {
3140 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3141 // wxASSERT (para != NULL);
5d7836c4
JS
3142
3143 if (para && para->GetChildCount() > 0)
3144 {
3145 // Stop searching if we're beyond the range of interest
3146 if (para->GetRange().GetStart() > range.GetEnd())
3147 break;
3148
3149 if (!para->GetRange().IsOutside(range))
3150 {
3151 // We'll be using a copy of the paragraph to make style changes,
3152 // not updating the buffer directly.
4e09ebe8 3153 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3154
5d7836c4
JS
3155 if (haveControl && withUndo)
3156 {
3157 newPara = new wxRichTextParagraph(*para);
3158 action->GetNewParagraphs().AppendChild(newPara);
3159
3160 // Also store the old ones for Undo
3161 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3162 }
3163 else
3164 newPara = para;
41a85215 3165
a7ed48a5
JS
3166 // If we're specifying paragraphs only, then we really mean character formatting
3167 // to be included in the paragraph style
3168 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3169 {
aeb6ebe2
JS
3170 if (removeStyle)
3171 {
3172 // Removes the given style from the paragraph
3173 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3174 }
3175 else if (resetExistingStyle)
523d2f14
JS
3176 newPara->GetAttributes() = wholeStyle;
3177 else
59509217 3178 {
523d2f14
JS
3179 if (applyMinimal)
3180 {
3181 // Only apply attributes that will make a difference to the combined
3182 // style as seen on the display
603f702b 3183 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3184 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3185 }
3186 else
3187 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3188 }
59509217 3189 }
5d7836c4 3190
5912d19e 3191 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3192 // since they will computed as needed. Only apply the character styling if it's _only_
3193 // character styling. This policy is subject to change and might be put under user control.
3194
59509217
JS
3195 // Hm. we might well be applying a mix of paragraph and character styles, in which
3196 // case we _do_ want to apply character styles regardless of what para styles are set.
3197 // But if we're applying a paragraph style, which has some character attributes, but
3198 // we only want the paragraphs to hold this character style, then we _don't_ want to
3199 // apply the character style. So we need to be able to choose.
3200
f1d800d9 3201 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3202 {
3203 wxRichTextRange childRange(range);
3204 childRange.LimitTo(newPara->GetRange());
7fe8059f 3205
5d7836c4
JS
3206 // Find the starting position and if necessary split it so
3207 // we can start applying a different style.
3208 // TODO: check that the style actually changes or is different
3209 // from style outside of range
4e09ebe8
JS
3210 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3211 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3212
5d7836c4
JS
3213 if (childRange.GetStart() == newPara->GetRange().GetStart())
3214 firstObject = newPara->GetChildren().GetFirst()->GetData();
3215 else
3216 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3217
5d7836c4
JS
3218 // Increment by 1 because we're apply the style one _after_ the split point
3219 long splitPoint = childRange.GetEnd();
3220 if (splitPoint != newPara->GetRange().GetEnd())
3221 splitPoint ++;
7fe8059f 3222
5d7836c4 3223 // Find last object
4b3483e7 3224 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3225 lastObject = newPara->GetChildren().GetLast()->GetData();
3226 else
3227 // lastObject is set as a side-effect of splitting. It's
3228 // returned as the object before the new object.
3229 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3230
5d7836c4
JS
3231 wxASSERT(firstObject != NULL);
3232 wxASSERT(lastObject != NULL);
7fe8059f 3233
5d7836c4
JS
3234 if (!firstObject || !lastObject)
3235 continue;
7fe8059f 3236
5d7836c4
JS
3237 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3238 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3239
4c9847e1
MW
3240 wxASSERT(firstNode);
3241 wxASSERT(lastNode);
7fe8059f 3242
5d7836c4 3243 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3244
5d7836c4
JS
3245 while (node2)
3246 {
3247 wxRichTextObject* child = node2->GetData();
7fe8059f 3248
aeb6ebe2
JS
3249 if (removeStyle)
3250 {
3251 // Removes the given style from the paragraph
3252 wxRichTextRemoveStyle(child->GetAttributes(), style);
3253 }
3254 else if (resetExistingStyle)
523d2f14
JS
3255 child->GetAttributes() = characterAttributes;
3256 else
59509217 3257 {
523d2f14
JS
3258 if (applyMinimal)
3259 {
3260 // Only apply attributes that will make a difference to the combined
3261 // style as seen on the display
603f702b 3262 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3263 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3264 }
3265 else
3266 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3267 }
59509217 3268
5d7836c4
JS
3269 if (node2 == lastNode)
3270 break;
7fe8059f 3271
5d7836c4
JS
3272 node2 = node2->GetNext();
3273 }
3274 }
3275 }
3276 }
3277
3278 node = node->GetNext();
3279 }
3280
3281 // Do action, or delay it until end of batch.
3282 if (haveControl && withUndo)
603f702b 3283 buffer->SubmitAction(action);
5d7836c4
JS
3284
3285 return true;
3286}
3287
603f702b
JS
3288// Just change the attributes for this single object.
3289void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3290{
603f702b 3291 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3292 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3293 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3294 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3295
cdaed652 3296 wxRichTextAction *action = NULL;
603f702b
JS
3297 wxRichTextAttr newAttr = obj->GetAttributes();
3298 if (resetExistingStyle)
3299 newAttr = textAttr;
3300 else
3301 newAttr.Apply(textAttr);
cdaed652
VZ
3302
3303 if (haveControl && withUndo)
3304 {
603f702b
JS
3305 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3306 action->SetRange(obj->GetRange().FromInternal());
3307 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3308 action->MakeObject(obj);
bec80f4f 3309
603f702b 3310 action->GetAttributes() = newAttr;
cdaed652
VZ
3311 }
3312 else
603f702b 3313 obj->GetAttributes() = newAttr;
cdaed652
VZ
3314
3315 if (haveControl && withUndo)
603f702b 3316 buffer->SubmitAction(action);
cdaed652
VZ
3317}
3318
5d7836c4 3319/// Get the text attributes for this position.
24777478 3320bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3321{
fe5aa22c
JS
3322 return DoGetStyle(position, style, true);
3323}
e191ee87 3324
24777478 3325bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3326{
3327 return DoGetStyle(position, style, false);
3328}
3329
fe5aa22c
JS
3330/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3331/// context attributes.
24777478 3332bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3333{
4e09ebe8 3334 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3335
5d7836c4 3336 if (style.IsParagraphStyle())
fe5aa22c 3337 {
5d7836c4 3338 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3339 if (obj)
3340 {
fe5aa22c
JS
3341 if (combineStyles)
3342 {
3343 // Start with the base style
3344 style = GetAttributes();
e191ee87 3345
fe5aa22c
JS
3346 // Apply the paragraph style
3347 wxRichTextApplyStyle(style, obj->GetAttributes());
3348 }
3349 else
3350 style = obj->GetAttributes();
5912d19e 3351
fe5aa22c
JS
3352 return true;
3353 }
5d7836c4
JS
3354 }
3355 else
fe5aa22c
JS
3356 {
3357 obj = GetLeafObjectAtPosition(position);
3358 if (obj)
3359 {
fe5aa22c
JS
3360 if (combineStyles)
3361 {
3362 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3363 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3364 }
3365 else
3366 style = obj->GetAttributes();
5912d19e 3367
fe5aa22c
JS
3368 return true;
3369 }
fe5aa22c
JS
3370 }
3371 return false;
5d7836c4
JS
3372}
3373
59509217
JS
3374static bool wxHasStyle(long flags, long style)
3375{
3376 return (flags & style) != 0;
3377}
3378
3379/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3380/// content.
24777478
JS
3381bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3382{
3383 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3384
3385 return true;
3386}
3387
3388/// Get the combined style for a range - if any attribute is different within the range,
3389/// that attribute is not present within the flags.
3390/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3391/// nested.
3392bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3393{
24777478
JS
3394 style = wxRichTextAttr();
3395
3396 wxRichTextAttr clashingAttr;
3397 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3398
24777478
JS
3399 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3400 while (node)
59509217 3401 {
603f702b
JS
3402 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3403 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3404 {
24777478 3405 if (para->GetChildren().GetCount() == 0)
59509217 3406 {
603f702b 3407 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3408
24777478 3409 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
59509217
JS
3410 }
3411 else
3412 {
24777478
JS
3413 wxRichTextRange paraRange(para->GetRange());
3414 paraRange.LimitTo(range);
59509217 3415
24777478
JS
3416 // First collect paragraph attributes only
3417 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3418 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3419 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
9c4cb611 3420
24777478
JS
3421 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3422
3423 while (childNode)
59509217 3424 {
24777478
JS
3425 wxRichTextObject* child = childNode->GetData();
3426 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3427 {
603f702b 3428 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3429
24777478
JS
3430 // Now collect character attributes only
3431 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3432
24777478
JS
3433 CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
3434 }
59509217 3435
24777478 3436 childNode = childNode->GetNext();
59509217
JS
3437 }
3438 }
59509217 3439 }
24777478 3440 node = node->GetNext();
59509217 3441 }
24777478
JS
3442 return true;
3443}
59509217 3444
24777478
JS
3445/// Set default style
3446bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3447{
3448 m_defaultAttributes = style;
3449 return true;
3450}
59509217 3451
24777478
JS
3452/// Test if this whole range has character attributes of the specified kind. If any
3453/// of the attributes are different within the range, the test fails. You
3454/// can use this to implement, for example, bold button updating. style must have
3455/// flags indicating which attributes are of interest.
3456bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3457{
3458 int foundCount = 0;
3459 int matchingCount = 0;
59509217 3460
24777478
JS
3461 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3462 while (node)
59509217 3463 {
24777478 3464 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3465 // wxASSERT (para != NULL);
59509217 3466
24777478 3467 if (para)
59509217 3468 {
24777478
JS
3469 // Stop searching if we're beyond the range of interest
3470 if (para->GetRange().GetStart() > range.GetEnd())
3471 return foundCount == matchingCount && foundCount != 0;
59509217 3472
24777478 3473 if (!para->GetRange().IsOutside(range))
59509217 3474 {
24777478 3475 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3476
24777478
JS
3477 while (node2)
3478 {
3479 wxRichTextObject* child = node2->GetData();
3480 // Allow for empty string if no buffer
3481 wxRichTextRange childRange = child->GetRange();
3482 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3483 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3484
24777478
JS
3485 if (!childRange.IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
3486 {
3487 foundCount ++;
3488 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3489
24777478
JS
3490 if (wxTextAttrEqPartial(textAttr, style))
3491 matchingCount ++;
3492 }
59509217 3493
24777478
JS
3494 node2 = node2->GetNext();
3495 }
59509217
JS
3496 }
3497 }
59509217 3498
24777478 3499 node = node->GetNext();
59509217
JS
3500 }
3501
24777478
JS
3502 return foundCount == matchingCount && foundCount != 0;
3503}
59509217 3504
24777478
JS
3505/// Test if this whole range has paragraph attributes of the specified kind. If any
3506/// of the attributes are different within the range, the test fails. You
3507/// can use this to implement, for example, centering button updating. style must have
3508/// flags indicating which attributes are of interest.
3509bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3510{
3511 int foundCount = 0;
3512 int matchingCount = 0;
3513
3514 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3515 while (node)
38f833b1 3516 {
24777478 3517 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3518 // wxASSERT (para != NULL);
24777478
JS
3519
3520 if (para)
38f833b1 3521 {
24777478
JS
3522 // Stop searching if we're beyond the range of interest
3523 if (para->GetRange().GetStart() > range.GetEnd())
3524 return foundCount == matchingCount && foundCount != 0;
3525
3526 if (!para->GetRange().IsOutside(range))
38f833b1 3527 {
24777478
JS
3528 wxRichTextAttr textAttr = GetAttributes();
3529 // Apply the paragraph style
3530 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3531
3532 foundCount ++;
3533 if (wxTextAttrEqPartial(textAttr, style))
3534 matchingCount ++;
38f833b1
JS
3535 }
3536 }
24777478
JS
3537
3538 node = node->GetNext();
38f833b1 3539 }
24777478
JS
3540 return foundCount == matchingCount && foundCount != 0;
3541}
5d7836c4 3542
cc2aecde
JS
3543void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3544{
3545 wxRichTextBuffer* buffer = GetBuffer();
3546 if (buffer && buffer->GetRichTextCtrl())
3547 buffer->GetRichTextCtrl()->PrepareContent(container);
3548}
3549
590a0f8b
JS
3550/// Set character or paragraph properties
3551bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3552{
3553 wxRichTextBuffer* buffer = GetBuffer();
3554
3555 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3556 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3557 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3558 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3559 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3560
3561 // If we are associated with a control, make undoable; otherwise, apply immediately
3562 // to the data.
3563
3564 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3565
3566 wxRichTextAction* action = NULL;
3567
3568 if (haveControl && withUndo)
3569 {
3570 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3571 action->SetRange(range);
3572 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3573 }
3574
3575 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3576 while (node)
3577 {
3578 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3579 // wxASSERT (para != NULL);
3580
3581 if (para && para->GetChildCount() > 0)
3582 {
3583 // Stop searching if we're beyond the range of interest
3584 if (para->GetRange().GetStart() > range.GetEnd())
3585 break;
3586
3587 if (!para->GetRange().IsOutside(range))
3588 {
3589 // We'll be using a copy of the paragraph to make style changes,
3590 // not updating the buffer directly.
3591 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3592
3593 if (haveControl && withUndo)
3594 {
3595 newPara = new wxRichTextParagraph(*para);
3596 action->GetNewParagraphs().AppendChild(newPara);
3597
3598 // Also store the old ones for Undo
3599 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3600 }
3601 else
3602 newPara = para;
3603
3604 if (parasOnly)
3605 {
3606 if (removeProperties)
3607 {
3608 // Removes the given style from the paragraph
3609 // TODO
3610 newPara->GetProperties().RemoveProperties(properties);
3611 }
3612 else if (resetExistingProperties)
3613 newPara->GetProperties() = properties;
3614 else
3615 newPara->GetProperties().MergeProperties(properties);
3616 }
3617
3618 // When applying paragraph styles dynamically, don't change the text objects' attributes
3619 // since they will computed as needed. Only apply the character styling if it's _only_
3620 // character styling. This policy is subject to change and might be put under user control.
3621
3622 // Hm. we might well be applying a mix of paragraph and character styles, in which
3623 // case we _do_ want to apply character styles regardless of what para styles are set.
3624 // But if we're applying a paragraph style, which has some character attributes, but
3625 // we only want the paragraphs to hold this character style, then we _don't_ want to
3626 // apply the character style. So we need to be able to choose.
3627
3628 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3629 {
3630 wxRichTextRange childRange(range);
3631 childRange.LimitTo(newPara->GetRange());
3632
3633 // Find the starting position and if necessary split it so
3634 // we can start applying different properties.
3635 // TODO: check that the properties actually change or are different
3636 // from properties outside of range
3637 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3638 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3639
3640 if (childRange.GetStart() == newPara->GetRange().GetStart())
3641 firstObject = newPara->GetChildren().GetFirst()->GetData();
3642 else
3643 firstObject = newPara->SplitAt(range.GetStart());
3644
3645 // Increment by 1 because we're apply the style one _after_ the split point
3646 long splitPoint = childRange.GetEnd();
3647 if (splitPoint != newPara->GetRange().GetEnd())
3648 splitPoint ++;
3649
3650 // Find last object
3651 if (splitPoint == newPara->GetRange().GetEnd())
3652 lastObject = newPara->GetChildren().GetLast()->GetData();
3653 else
3654 // lastObject is set as a side-effect of splitting. It's
3655 // returned as the object before the new object.
3656 (void) newPara->SplitAt(splitPoint, & lastObject);
3657
3658 wxASSERT(firstObject != NULL);
3659 wxASSERT(lastObject != NULL);
3660
3661 if (!firstObject || !lastObject)
3662 continue;
3663
3664 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3665 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3666
3667 wxASSERT(firstNode);
3668 wxASSERT(lastNode);
3669
3670 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3671
3672 while (node2)
3673 {
3674 wxRichTextObject* child = node2->GetData();
3675
3676 if (removeProperties)
3677 {
3678 // Removes the given properties from the paragraph
3679 child->GetProperties().RemoveProperties(properties);
3680 }
3681 else if (resetExistingProperties)
3682 child->GetProperties() = properties;
3683 else
3684 {
3685 child->GetProperties().MergeProperties(properties);
3686 }
3687
3688 if (node2 == lastNode)
3689 break;
3690
3691 node2 = node2->GetNext();
3692 }
3693 }
3694 }
3695 }
3696
3697 node = node->GetNext();
3698 }
3699
3700 // Do action, or delay it until end of batch.
3701 if (haveControl && withUndo)
3702 buffer->SubmitAction(action);
3703
3704 return true;
3705}
3706
5d7836c4
JS
3707void wxRichTextParagraphLayoutBox::Reset()
3708{
3709 Clear();
3710
603f702b
JS
3711 wxRichTextBuffer* buffer = GetBuffer();
3712 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3713 {
603f702b
JS
3714 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3715 event.SetEventObject(buffer->GetRichTextCtrl());
3716 event.SetContainer(this);
cd8ba0d9
JS
3717
3718 buffer->SendEvent(event, true);
3719 }
3720
7fe8059f 3721 AddParagraph(wxEmptyString);
3e541562 3722
cc2aecde
JS
3723 PrepareContent(*this);
3724
603f702b 3725 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3726}
3727
38113684
JS
3728/// Invalidate the buffer. With no argument, invalidates whole buffer.
3729void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3730{
603f702b 3731 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3732
603f702b
JS
3733 DoInvalidate(invalidRange);
3734}
3735
3736// Do the (in)validation for this object only
3737void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3738{
1e967276 3739 if (invalidRange == wxRICHTEXT_ALL)
38113684 3740 {
1e967276 3741 m_invalidRange = wxRICHTEXT_ALL;
38113684 3742 }
1e967276 3743 // Already invalidating everything
603f702b
JS
3744 else if (m_invalidRange == wxRICHTEXT_ALL)
3745 {
3746 }
3747 else
3748 {
3749 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3750 m_invalidRange.SetStart(invalidRange.GetStart());
3751 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3752 m_invalidRange.SetEnd(invalidRange.GetEnd());
3753 }
3754}
39a1c2f2 3755
603f702b
JS
3756// Do the (in)validation both up and down the hierarchy
3757void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3758{
3759 Invalidate(invalidRange);
3760
3761 if (invalidRange != wxRICHTEXT_NONE)
3762 {
3763 // Now go up the hierarchy
3764 wxRichTextObject* thisObj = this;
3765 wxRichTextObject* p = GetParent();
3766 while (p)
3767 {
3768 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3769 if (l)
3770 l->DoInvalidate(thisObj->GetRange());
3771
3772 thisObj = p;
3773 p = p->GetParent();
3774 }
3775 }
38113684
JS
3776}
3777
3778/// Get invalid range, rounding to entire paragraphs if argument is true.
3779wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3780{
1e967276 3781 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3782 return m_invalidRange;
39a1c2f2 3783
38113684 3784 wxRichTextRange range = m_invalidRange;
39a1c2f2 3785
38113684
JS
3786 if (wholeParagraphs)
3787 {
3788 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3789 if (para1)
3790 range.SetStart(para1->GetRange().GetStart());
cdaed652 3791 // floating layout make all child should be relayout
603f702b 3792 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3793 }
3794 return range;
3795}
3796
fe5aa22c
JS
3797/// Apply the style sheet to the buffer, for example if the styles have changed.
3798bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3799{
3800 wxASSERT(styleSheet != NULL);
3801 if (!styleSheet)
3802 return false;
3803
3804 int foundCount = 0;
3805
44580804
JS
3806 wxRichTextAttr attr(GetBasicStyle());
3807 if (GetBasicStyle().HasParagraphStyleName())
3808 {
3809 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3810 if (paraDef)
3811 {
3812 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3813 SetBasicStyle(attr);
3814 foundCount ++;
3815 }
3816 }
3817
3818 if (GetBasicStyle().HasCharacterStyleName())
3819 {
3820 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3821 if (charDef)
3822 {
3823 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3824 SetBasicStyle(attr);
3825 foundCount ++;
3826 }
3827 }
3828
fe5aa22c
JS
3829 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3830 while (node)
3831 {
3832 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3833 // wxASSERT (para != NULL);
fe5aa22c
JS
3834
3835 if (para)
3836 {
38f833b1
JS
3837 // Combine paragraph and list styles. If there is a list style in the original attributes,
3838 // the current indentation overrides anything else and is used to find the item indentation.
3839 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3840 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3841 // exception as above).
3842 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3843 // So when changing a list style interactively, could retrieve level based on current style, then
3844 // set appropriate indent and apply new style.
41a85215 3845
bbd55ff9
JS
3846 int outline = -1;
3847 int num = -1;
3848 if (para->GetAttributes().HasOutlineLevel())
3849 outline = para->GetAttributes().GetOutlineLevel();
3850 if (para->GetAttributes().HasBulletNumber())
3851 num = para->GetAttributes().GetBulletNumber();
3852
38f833b1
JS
3853 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3854 {
3855 int currentIndent = para->GetAttributes().GetLeftIndent();
3856
3857 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3858 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3859 if (paraDef && !listDef)
3860 {
336d8ae9 3861 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3862 foundCount ++;
3863 }
3864 else if (listDef && !paraDef)
3865 {
3866 // Set overall style defined for the list style definition
336d8ae9 3867 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3868
3869 // Apply the style for this level
3870 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3871 foundCount ++;
3872 }
3873 else if (listDef && paraDef)
3874 {
3875 // Combines overall list style, style for level, and paragraph style
336d8ae9 3876 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3877 foundCount ++;
3878 }
3879 }
3880 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3881 {
3882 int currentIndent = para->GetAttributes().GetLeftIndent();
3883
3884 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3885
41a85215 3886 // Overall list definition style
336d8ae9 3887 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3888
38f833b1
JS
3889 // Style for this level
3890 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3891
3892 foundCount ++;
3893 }
3894 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3895 {
3896 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3897 if (def)
3898 {
336d8ae9 3899 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3900 foundCount ++;
3901 }
3902 }
bbd55ff9
JS
3903
3904 if (outline != -1)
3905 para->GetAttributes().SetOutlineLevel(outline);
3906 if (num != -1)
3907 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3908 }
3909
3910 node = node->GetNext();
3911 }
3912 return foundCount != 0;
3913}
3914
38f833b1
JS
3915/// Set list style
3916bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3917{
603f702b
JS
3918 wxRichTextBuffer* buffer = GetBuffer();
3919 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 3920
38f833b1
JS
3921 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3922 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3923 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3924 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 3925
38f833b1
JS
3926 // Current number, if numbering
3927 int n = startFrom;
41a85215 3928
38f833b1
JS
3929 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3930
3931 // If we are associated with a control, make undoable; otherwise, apply immediately
3932 // to the data.
3933
603f702b 3934 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
3935
3936 wxRichTextAction* action = NULL;
3937
3938 if (haveControl && withUndo)
3939 {
603f702b 3940 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 3941 action->SetRange(range);
603f702b 3942 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
3943 }
3944
3945 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3946 while (node)
3947 {
3948 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3949 // wxASSERT (para != NULL);
38f833b1
JS
3950
3951 if (para && para->GetChildCount() > 0)
3952 {
3953 // Stop searching if we're beyond the range of interest
3954 if (para->GetRange().GetStart() > range.GetEnd())
3955 break;
3956
3957 if (!para->GetRange().IsOutside(range))
3958 {
3959 // We'll be using a copy of the paragraph to make style changes,
3960 // not updating the buffer directly.
3961 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3962
3963 if (haveControl && withUndo)
3964 {
3965 newPara = new wxRichTextParagraph(*para);
3966 action->GetNewParagraphs().AppendChild(newPara);
3967
3968 // Also store the old ones for Undo
3969 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3970 }
3971 else
3972 newPara = para;
41a85215 3973
38f833b1
JS
3974 if (def)
3975 {
3976 int thisIndent = newPara->GetAttributes().GetLeftIndent();
3977 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 3978
38f833b1
JS
3979 // How is numbering going to work?
3980 // If we are renumbering, or numbering for the first time, we need to keep
3981 // track of the number for each level. But we might be simply applying a different
3982 // list style.
3983 // In Word, applying a style to several paragraphs, even if at different levels,
3984 // reverts the level back to the same one. So we could do the same here.
3985 // Renumbering will need to be done when we promote/demote a paragraph.
3986
3987 // Apply the overall list style, and item style for this level
24777478 3988 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 3989 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 3990
d2d0adc7 3991 // Now we need to do numbering
38f833b1
JS
3992 if (renumber)
3993 {
3994 newPara->GetAttributes().SetBulletNumber(n);
3995 }
41a85215 3996
38f833b1
JS
3997 n ++;
3998 }
3999 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4000 {
4001 // if def is NULL, remove list style, applying any associated paragraph style
4002 // to restore the attributes
4003
4004 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4005 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 4006 newPara->GetAttributes().SetBulletText(wxEmptyString);
41a85215 4007
38f833b1 4008 // Eliminate the main list-related attributes
d2d0adc7 4009 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 4010
38f833b1
JS
4011 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4012 {
4013 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4014 if (def)
4015 {
336d8ae9 4016 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
4017 }
4018 }
4019 }
4020 }
4021 }
4022
4023 node = node->GetNext();
4024 }
4025
4026 // Do action, or delay it until end of batch.
4027 if (haveControl && withUndo)
603f702b 4028 buffer->SubmitAction(action);
38f833b1
JS
4029
4030 return true;
4031}
4032
4033bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4034{
603f702b
JS
4035 wxRichTextBuffer* buffer = GetBuffer();
4036 if (buffer && buffer->GetStyleSheet())
38f833b1 4037 {
603f702b 4038 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4039 if (def)
4040 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4041 }
4042 return false;
4043}
4044
4045/// Clear list for given range
4046bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4047{
4048 return SetListStyle(range, NULL, flags);
4049}
4050
4051/// Number/renumber any list elements in the given range
4052bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4053{
4054 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4055}
4056
4057/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4058bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4059 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4060{
603f702b
JS
4061 wxRichTextBuffer* buffer = GetBuffer();
4062 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4063
38f833b1
JS
4064 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4065 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 4066#if wxDEBUG_LEVEL
38f833b1 4067 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 4068#endif
38f833b1
JS
4069
4070 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4071
38f833b1
JS
4072 // Max number of levels
4073 const int maxLevels = 10;
41a85215 4074
38f833b1
JS
4075 // The level we're looking at now
4076 int currentLevel = -1;
41a85215 4077
38f833b1
JS
4078 // The item number for each level
4079 int levels[maxLevels];
4080 int i;
41a85215 4081
38f833b1
JS
4082 // Reset all numbering
4083 for (i = 0; i < maxLevels; i++)
4084 {
4085 if (startFrom != -1)
d2d0adc7 4086 levels[i] = startFrom-1;
38f833b1 4087 else if (renumber) // start again
d2d0adc7 4088 levels[i] = 0;
38f833b1
JS
4089 else
4090 levels[i] = -1; // start from the number we found, if any
4091 }
41a85215 4092
bb7bbd12 4093#if wxDEBUG_LEVEL
38f833b1 4094 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
bb7bbd12 4095#endif
38f833b1
JS
4096
4097 // If we are associated with a control, make undoable; otherwise, apply immediately
4098 // to the data.
4099
603f702b 4100 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4101
4102 wxRichTextAction* action = NULL;
4103
4104 if (haveControl && withUndo)
4105 {
603f702b 4106 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4107 action->SetRange(range);
603f702b 4108 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4109 }
4110
4111 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4112 while (node)
4113 {
4114 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4115 // wxASSERT (para != NULL);
38f833b1
JS
4116
4117 if (para && para->GetChildCount() > 0)
4118 {
4119 // Stop searching if we're beyond the range of interest
4120 if (para->GetRange().GetStart() > range.GetEnd())
4121 break;
4122
4123 if (!para->GetRange().IsOutside(range))
4124 {
4125 // We'll be using a copy of the paragraph to make style changes,
4126 // not updating the buffer directly.
4127 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4128
4129 if (haveControl && withUndo)
4130 {
4131 newPara = new wxRichTextParagraph(*para);
4132 action->GetNewParagraphs().AppendChild(newPara);
4133
4134 // Also store the old ones for Undo
4135 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4136 }
4137 else
4138 newPara = para;
41a85215 4139
38f833b1
JS
4140 wxRichTextListStyleDefinition* defToUse = def;
4141 if (!defToUse)
4142 {
336d8ae9
VZ
4143 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4144 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 4145 }
41a85215 4146
38f833b1
JS
4147 if (defToUse)
4148 {
4149 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4150 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4151
d2d0adc7
JS
4152 // If we've specified a level to apply to all, change the level.
4153 if (specifiedLevel != -1)
38f833b1 4154 thisLevel = specifiedLevel;
41a85215 4155
38f833b1
JS
4156 // Do promotion if specified
4157 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4158 {
4159 thisLevel = thisLevel - promoteBy;
4160 if (thisLevel < 0)
4161 thisLevel = 0;
4162 if (thisLevel > 9)
4163 thisLevel = 9;
4164 }
41a85215 4165
38f833b1 4166 // Apply the overall list style, and item style for this level
24777478 4167 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4168 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4169
38f833b1 4170 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 4171
38f833b1
JS
4172 if (currentLevel == -1)
4173 currentLevel = thisLevel;
41a85215 4174
38f833b1
JS
4175 // Same level as before, do nothing except increment level's number afterwards
4176 if (currentLevel == thisLevel)
4177 {
4178 }
4179 // A deeper level: start renumbering all levels after current level
4180 else if (thisLevel > currentLevel)
4181 {
4182 for (i = currentLevel+1; i <= thisLevel; i++)
4183 {
d2d0adc7 4184 levels[i] = 0;
38f833b1
JS
4185 }
4186 currentLevel = thisLevel;
4187 }
4188 else if (thisLevel < currentLevel)
4189 {
4190 currentLevel = thisLevel;
41a85215 4191 }
38f833b1
JS
4192
4193 // Use the current numbering if -1 and we have a bullet number already
4194 if (levels[currentLevel] == -1)
4195 {
4196 if (newPara->GetAttributes().HasBulletNumber())
4197 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4198 else
4199 levels[currentLevel] = 1;
4200 }
d2d0adc7
JS
4201 else
4202 {
4203 levels[currentLevel] ++;
4204 }
41a85215 4205
38f833b1
JS
4206 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4207
d2d0adc7
JS
4208 // Create the bullet text if an outline list
4209 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4210 {
4211 wxString text;
4212 for (i = 0; i <= currentLevel; i++)
4213 {
4214 if (!text.IsEmpty())
4215 text += wxT(".");
4216 text += wxString::Format(wxT("%d"), levels[i]);
4217 }
4218 newPara->GetAttributes().SetBulletText(text);
4219 }
38f833b1
JS
4220 }
4221 }
4222 }
4223
4224 node = node->GetNext();
4225 }
4226
4227 // Do action, or delay it until end of batch.
4228 if (haveControl && withUndo)
603f702b 4229 buffer->SubmitAction(action);
38f833b1
JS
4230
4231 return true;
4232}
4233
4234bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4235{
603f702b
JS
4236 wxRichTextBuffer* buffer = GetBuffer();
4237 if (buffer->GetStyleSheet())
38f833b1
JS
4238 {
4239 wxRichTextListStyleDefinition* def = NULL;
4240 if (!defName.IsEmpty())
603f702b 4241 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4242 return NumberList(range, def, flags, startFrom, specifiedLevel);
4243 }
4244 return false;
4245}
4246
4247/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4248bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4249{
4250 // TODO
4251 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4252 // to NumberList with a flag indicating promotion is required within one of the ranges.
4253 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4254 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4255 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4256 // list position will start from 1.
4257 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4258 // We can end the renumbering at this point.
41a85215 4259
38f833b1 4260 // For now, only renumber within the promotion range.
41a85215 4261
38f833b1
JS
4262 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4263}
4264
4265bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4266{
603f702b
JS
4267 wxRichTextBuffer* buffer = GetBuffer();
4268 if (buffer->GetStyleSheet())
38f833b1
JS
4269 {
4270 wxRichTextListStyleDefinition* def = NULL;
4271 if (!defName.IsEmpty())
603f702b 4272 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4273 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4274 }
4275 return false;
4276}
4277
d2d0adc7
JS
4278/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4279/// position of the paragraph that it had to start looking from.
24777478 4280bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 4281{
d2d0adc7
JS
4282 if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4283 return false;
3e541562 4284
603f702b
JS
4285 wxRichTextBuffer* buffer = GetBuffer();
4286 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4287 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 4288 {
336d8ae9 4289 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
4290 if (def)
4291 {
4292 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4293 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4294
d2d0adc7
JS
4295 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4296
4297 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4298 if (previousParagraph->GetAttributes().HasBulletName())
4299 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4300 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4301 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4302
d2d0adc7
JS
4303 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4304 attr.SetBulletNumber(nextNumber);
3e541562 4305
d2d0adc7
JS
4306 if (isOutline)
4307 {
4308 wxString text = previousParagraph->GetAttributes().GetBulletText();
4309 if (!text.IsEmpty())
4310 {
4311 int pos = text.Find(wxT('.'), true);
4312 if (pos != wxNOT_FOUND)
4313 {
4314 text = text.Mid(0, text.Length() - pos - 1);
4315 }
4316 else
4317 text = wxEmptyString;
4318 if (!text.IsEmpty())
4319 text += wxT(".");
4320 text += wxString::Format(wxT("%d"), nextNumber);
4321 attr.SetBulletText(text);
4322 }
4323 }
3e541562 4324
d2d0adc7
JS
4325 return true;
4326 }
4327 else
4328 return false;
4329 }
4330 else
4331 return false;
4332}
4333
5d7836c4
JS
4334/*!
4335 * wxRichTextParagraph
4336 * This object represents a single paragraph (or in a straight text editor, a line).
4337 */
4338
603f702b 4339IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4340
cfa3b256
JS
4341wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4342
24777478 4343wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4344 wxRichTextCompositeObject(parent)
5d7836c4 4345{
5d7836c4
JS
4346 if (style)
4347 SetAttributes(*style);
4348}
4349
24777478 4350wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4351 wxRichTextCompositeObject(parent)
5d7836c4 4352{
4f32b3cf
JS
4353 if (paraStyle)
4354 SetAttributes(*paraStyle);
5d7836c4 4355
4f32b3cf 4356 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4357}
4358
4359wxRichTextParagraph::~wxRichTextParagraph()
4360{
4361 ClearLines();
4362}
4363
4364/// Draw the item
8db2e3ef 4365bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4366{
603f702b
JS
4367 if (!IsShown())
4368 return true;
4369
4370 // Currently we don't merge these attributes with the parent, but we
4371 // should consider whether we should (e.g. if we set a border colour
4372 // for all paragraphs). But generally box attributes are likely to be
4373 // different for different objects.
4374 wxRect paraRect = GetRect();
24777478 4375 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef
JS
4376 context.ApplyVirtualAttributes(attr, this);
4377
4378 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
fe5aa22c 4379
5d7836c4 4380 // Draw the bullet, if any
fe5aa22c 4381 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4 4382 {
fe5aa22c 4383 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4384 {
fe5aa22c 4385 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4386 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4387
8db2e3ef 4388 wxRichTextAttr bulletAttr(attr);
d2d0adc7 4389
e3eac0ff
JS
4390 // Combine with the font of the first piece of content, if one is specified
4391 if (GetChildren().GetCount() > 0)
4392 {
4393 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4394 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4395 {
4396 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4397 }
4398 }
4399
d2d0adc7 4400 // Get line height from first line, if any
d3b9f782 4401 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4402
4403 wxPoint linePos;
4404 int lineHeight wxDUMMY_INITIALIZE(0);
4405 if (line)
5d7836c4 4406 {
d2d0adc7
JS
4407 lineHeight = line->GetSize().y;
4408 linePos = line->GetPosition() + GetPosition();
5d7836c4 4409 }
d2d0adc7 4410 else
f089713f 4411 {
f089713f 4412 wxFont font;
44cc96a8
JS
4413 if (bulletAttr.HasFont() && GetBuffer())
4414 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4415 else
4416 font = (*wxNORMAL_FONT);
4417
ecb5fbf1 4418 wxCheckSetFont(dc, font);
f089713f 4419
d2d0adc7
JS
4420 lineHeight = dc.GetCharHeight();
4421 linePos = GetPosition();
4422 linePos.y += spaceBeforePara;
4423 }
f089713f 4424
d2d0adc7 4425 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4426
d2d0adc7
JS
4427 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4428 {
4429 if (wxRichTextBuffer::GetRenderer())
4430 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4431 }
3e541562
JS
4432 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4433 {
d2d0adc7
JS
4434 if (wxRichTextBuffer::GetRenderer())
4435 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4436 }
5d7836c4
JS
4437 else
4438 {
4439 wxString bulletText = GetBulletText();
3e541562 4440
d2d0adc7
JS
4441 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4442 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4443 }
4444 }
4445 }
7fe8059f 4446
5d7836c4
JS
4447 // Draw the range for each line, one object at a time.
4448
4449 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4450 while (node)
4451 {
4452 wxRichTextLine* line = node->GetData();
1e967276 4453 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4454
5d7836c4
JS
4455 // Lines are specified relative to the paragraph
4456
4457 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4458
7051fa41
JS
4459 // Don't draw if off the screen
4460 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4461 {
7051fa41
JS
4462 wxPoint objectPosition = linePosition;
4463 int maxDescent = line->GetDescent();
4464
4465 // Loop through objects until we get to the one within range
4466 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4467
7051fa41
JS
4468 int i = 0;
4469 while (node2)
5d7836c4 4470 {
7051fa41 4471 wxRichTextObject* child = node2->GetData();
5d7836c4 4472
cdaed652 4473 if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4474 {
7051fa41
JS
4475 // Draw this part of the line at the correct position
4476 wxRichTextRange objectRange(child->GetRange());
4477 objectRange.LimitTo(lineRange);
4478
4479 wxSize objectSize;
603f702b 4480 if (child->IsTopLevel())
7051fa41 4481 {
603f702b
JS
4482 objectSize = child->GetCachedSize();
4483 objectRange = child->GetOwnRange();
7051fa41
JS
4484 }
4485 else
7051fa41 4486 {
603f702b
JS
4487#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4488 if (i < (int) line->GetObjectSizes().GetCount())
4489 {
4490 objectSize.x = line->GetObjectSizes()[(size_t) i];
4491 }
4492 else
4493#endif
4494 {
4495 int descent = 0;
8db2e3ef 4496 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
603f702b 4497 }
7051fa41 4498 }
5d7836c4 4499
7051fa41
JS
4500 // Use the child object's width, but the whole line's height
4501 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
8db2e3ef 4502 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4503
7051fa41
JS
4504 objectPosition.x += objectSize.x;
4505 i ++;
4506 }
4507 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4508 // Can break out of inner loop now since we've passed this line's range
4509 break;
5d7836c4 4510
7051fa41
JS
4511 node2 = node2->GetNext();
4512 }
5d7836c4
JS
4513 }
4514
4515 node = node->GetNext();
7fe8059f 4516 }
5d7836c4
JS
4517
4518 return true;
4519}
4520
4f3d5bc0
JS
4521// Get the range width using partial extents calculated for the whole paragraph.
4522static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4523{
4524 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4525
affbfa1f
JS
4526 if (partialExtents.GetCount() < (size_t) range.GetLength())
4527 return 0;
4528
4f3d5bc0
JS
4529 int leftMostPos = 0;
4530 if (range.GetStart() - para.GetRange().GetStart() > 0)
4531 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4532
4533 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4534
4535 int w = rightMostPos - leftMostPos;
4536
4537 return w;
4538}
4539
5d7836c4 4540/// Lay the item out
8db2e3ef 4541bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 4542{
cdaed652
VZ
4543 // Deal with floating objects firstly before the normal layout
4544 wxRichTextBuffer* buffer = GetBuffer();
4545 wxASSERT(buffer);
07d4142f 4546 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
cdaed652 4547 wxASSERT(collector);
8db2e3ef 4548 LayoutFloat(dc, context, rect, style, collector);
cdaed652 4549
24777478 4550 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef 4551 context.ApplyVirtualAttributes(attr, this);
fe5aa22c 4552
169adfa9
JS
4553 // ClearLines();
4554
5d7836c4 4555 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4556 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4557 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4558 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4559 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4560 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4561
4562 int lineSpacing = 0;
4563
4564 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
a1b806b9 4565 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
5d7836c4 4566 {
8f0e4366
JS
4567 wxCheckSetFont(dc, attr.GetFont());
4568 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
5d7836c4
JS
4569 }
4570
5d7836c4
JS
4571 // Start position for each line relative to the paragraph
4572 int startPositionFirstLine = leftIndent;
4573 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4574
4575 // If we have a bullet in this paragraph, the start position for the first line's text
4576 // is actually leftIndent + leftSubIndent.
fe5aa22c 4577 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4578 startPositionFirstLine = startPositionSubsequentLines;
4579
5d7836c4
JS
4580 long lastEndPos = GetRange().GetStart()-1;
4581 long lastCompletedEndPos = lastEndPos;
4582
4583 int currentWidth = 0;
4584 SetPosition(rect.GetPosition());
4585
4586 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4587 int lineHeight = 0;
4588 int maxWidth = 0;
603f702b 4589 int maxHeight = currentPosition.y;
476a319a 4590 int maxAscent = 0;
5d7836c4 4591 int maxDescent = 0;
5d7836c4 4592 int lineCount = 0;
cdaed652
VZ
4593 int lineAscent = 0;
4594 int lineDescent = 0;
5d7836c4 4595
2f45f554
JS
4596 wxRichTextObjectList::compatibility_iterator node;
4597
4598#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4599 wxUnusedVar(style);
4600 wxArrayInt partialExtents;
4601
4602 wxSize paraSize;
8aab23a1 4603 int paraDescent = 0;
2f45f554
JS
4604
4605 // This calculates the partial text extents
8db2e3ef 4606 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
2f45f554
JS
4607#else
4608 node = m_children.GetFirst();
ecb5fbf1
JS
4609 while (node)
4610 {
4611 wxRichTextObject* child = node->GetData();
4612
603f702b 4613 //child->SetCachedSize(wxDefaultSize);
8db2e3ef 4614 child->Layout(dc, context, rect, style);
ecb5fbf1
JS
4615
4616 node = node->GetNext();
4617 }
31778480
JS
4618#endif
4619
5d7836c4
JS
4620 // Split up lines
4621
4622 // We may need to go back to a previous child, in which case create the new line,
4623 // find the child corresponding to the start position of the string, and
4624 // continue.
4625
603f702b
JS
4626 wxRect availableRect;
4627
ecb5fbf1 4628 node = m_children.GetFirst();
5d7836c4
JS
4629 while (node)
4630 {
4631 wxRichTextObject* child = node->GetData();
4632
cdaed652 4633 // If floating, ignore. We already laid out floats.
603f702b
JS
4634 // Also ignore if empty object, except if we haven't got any
4635 // size yet.
4636 if (child->IsFloating() || !child->IsShown() ||
4637 (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4638 )
affbfa1f
JS
4639 {
4640 node = node->GetNext();
4641 continue;
4642 }
4643
5d7836c4
JS
4644 // If this is e.g. a composite text box, it will need to be laid out itself.
4645 // But if just a text fragment or image, for example, this will
4646 // do nothing. NB: won't we need to set the position after layout?
4647 // since for example if position is dependent on vertical line size, we
4648 // can't tell the position until the size is determined. So possibly introduce
4649 // another layout phase.
4650
5d7836c4
JS
4651 // We may only be looking at part of a child, if we searched back for wrapping
4652 // and found a suitable point some way into the child. So get the size for the fragment
4653 // if necessary.
3e541562 4654
ff76711f
JS
4655 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4656 long lastPosToUse = child->GetRange().GetEnd();
4657 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4658
ff76711f
JS
4659 if (lineBreakInThisObject)
4660 lastPosToUse = nextBreakPos;
5d7836c4
JS
4661
4662 wxSize childSize;
4663 int childDescent = 0;
3e541562 4664
603f702b
JS
4665 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4666 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4667 rect.width - startOffset - rightIndent, rect.height);
4668
4669 if (child->IsTopLevel())
4670 {
4671 wxSize oldSize = child->GetCachedSize();
4672
4673 child->Invalidate(wxRICHTEXT_ALL);
4674 child->SetPosition(wxPoint(0, 0));
4675
4676 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4677 // lays out the object again using the minimum size
4678 // The position will be determined by its location in its line,
4679 // and not by the child's actual position.
8db2e3ef
JS
4680 child->LayoutToBestSize(dc, context, buffer,
4681 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4682
4683 if (oldSize != child->GetCachedSize())
4684 {
4685 partialExtents.Clear();
4686
4687 // Recalculate the partial text extents since the child object changed size
8db2e3ef 4688 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
603f702b
JS
4689 }
4690 }
4691
4692 // Problem: we need to layout composites here for which we need the available width,
4693 // but we can't get the available width without using the float collector which
4694 // needs to know the object height.
4695
ff76711f 4696 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4697 {
4698 childSize = child->GetCachedSize();
4699 childDescent = child->GetDescent();
4700 }
4701 else
4f3d5bc0
JS
4702 {
4703#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4704 // Get height only, then the width using the partial extents
8db2e3ef 4705 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4f3d5bc0
JS
4706 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4707#else
8db2e3ef 4708 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
4f3d5bc0
JS
4709#endif
4710 }
ff76711f 4711
603f702b
JS
4712 bool doLoop = true;
4713 int loopIterations = 0;
4714
4715 // If there are nested objects that need to lay themselves out, we have to do this in a
4716 // loop because the height of the object may well depend on the available width.
4717 // And because of floating object positioning, the available width depends on the
4718 // height of the object and whether it will clash with the floating objects.
4719 // So, we see whether the available width changes due to the presence of floating images.
4720 // If it does, then we'll use the new restricted width to find the object height again.
4721 // If this causes another restriction in the available width, we'll try again, until
4722 // either we lose patience or the available width settles down.
4723 do
4724 {
4725 loopIterations ++;
4726
4727 wxRect oldAvailableRect = availableRect;
4728
4729 // Available width depends on the floating objects and the line height.
7c9fdebe 4730 // Note: the floating objects may be placed vertically along the two sides of
603f702b
JS
4731 // buffer, so we may have different available line widths with different
4732 // [startY, endY]. So, we can't determine how wide the available
4733 // space is until we know the exact line height.
a70eb13e
JS
4734 if (childDescent == 0)
4735 {
4736 lineHeight = wxMax(lineHeight, childSize.y);
4737 lineDescent = maxDescent;
4738 lineAscent = maxAscent;
4739 }
4740 else
4741 {
4742 lineDescent = wxMax(childDescent, maxDescent);
4743 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4744 }
4745 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
603f702b
JS
4746 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4747
4748 // Adjust availableRect to the space that is available when taking floating objects into account.
4749
4750 if (floatAvailableRect.x + startOffset > availableRect.x)
4751 {
4752 int newX = floatAvailableRect.x + startOffset;
4753 int newW = availableRect.width - (newX - availableRect.x);
4754 availableRect.x = newX;
4755 availableRect.width = newW;
4756 }
4757
4758 if (floatAvailableRect.width < availableRect.width)
4759 availableRect.width = floatAvailableRect.width;
4760
4761 currentPosition.x = availableRect.x - rect.x;
4762
4763 if (child->IsTopLevel() && loopIterations <= 20)
4764 {
4765 if (availableRect != oldAvailableRect)
4766 {
4767 wxSize oldSize = child->GetCachedSize();
4768
603f702b
JS
4769 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4770 // lays out the object again using the minimum size
4771 child->Invalidate(wxRICHTEXT_ALL);
8db2e3ef
JS
4772 child->LayoutToBestSize(dc, context, buffer,
4773 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4774 childSize = child->GetCachedSize();
4775 childDescent = child->GetDescent();
603f702b
JS
4776
4777 if (oldSize != child->GetCachedSize())
4778 {
4779 partialExtents.Clear();
4780
4781 // Recalculate the partial text extents since the child object changed size
8db2e3ef 4782 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
603f702b 4783 }
cdaed652 4784
603f702b
JS
4785 // Go around the loop finding the available rect for the given floating objects
4786 }
4787 else
4788 doLoop = false;
4789 }
4790 else
4791 doLoop = false;
4792 }
4793 while (doLoop);
cdaed652 4794
20d09da5
JS
4795 if (child->IsTopLevel())
4796 {
4797 // We can move it to the correct position at this point
4798 child->Move(GetPosition() + wxPoint(currentWidth, currentPosition.y));
4799 }
4800
ff76711f
JS
4801 // Cases:
4802 // 1) There was a line break BEFORE the natural break
4803 // 2) There was a line break AFTER the natural break
603f702b
JS
4804 // 3) It's the last line
4805 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4806
603f702b
JS
4807 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4808 ||
4809 (childSize.x + currentWidth > availableRect.width)
4810 ||
4811 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4812
4813 )
5d7836c4
JS
4814 {
4815 long wrapPosition = 0;
603f702b
JS
4816 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4817 wrapPosition = child->GetRange().GetEnd();
4818 else
5d7836c4
JS
4819
4820 // Find a place to wrap. This may walk back to previous children,
4821 // for example if a word spans several objects.
cdaed652
VZ
4822 // Note: one object must contains only one wxTextAtrr, so the line height will not
4823 // change inside one object. Thus, we can pass the remain line width to the
4824 // FindWrapPosition function.
8db2e3ef 4825 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4826 {
4827 // If the function failed, just cut it off at the end of this child.
4828 wrapPosition = child->GetRange().GetEnd();
4829 }
4830
4831 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4832 if (wrapPosition <= lastCompletedEndPos)
4833 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4834
603f702b
JS
4835 // Line end position shouldn't be the same as the end, or greater.
4836 if (wrapPosition >= GetRange().GetEnd())
4837 wrapPosition = GetRange().GetEnd()-1;
4838
5d7836c4 4839 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4840
5d7836c4
JS
4841 // Let's find the actual size of the current line now
4842 wxSize actualSize;
4843 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4844
a70eb13e 4845 childDescent = 0;
4ab8a5e2 4846
4f3d5bc0 4847#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4848 if (!child->IsEmpty())
4849 {
4850 // Get height only, then the width using the partial extents
8db2e3ef 4851 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
603f702b
JS
4852 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4853 }
4854 else
4f3d5bc0 4855#endif
8db2e3ef 4856 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
4f3d5bc0 4857
5d7836c4 4858 currentWidth = actualSize.x;
a70eb13e
JS
4859
4860 // The descent for the whole line at this point, is the correct max descent
4861 maxDescent = childDescent;
4862 // Maximum ascent
4863 maxAscent = actualSize.y-childDescent;
4864
4865 // lineHeight is given by the height for the whole line, since it will
4866 // take into account ascend/descend.
4867 lineHeight = actualSize.y;
7fe8059f 4868
07d4142f 4869 if (lineHeight == 0 && buffer)
603f702b 4870 {
07d4142f 4871 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
4872 wxCheckSetFont(dc, font);
4873 lineHeight = dc.GetCharHeight();
4874 }
4875
4876 if (maxDescent == 0)
4877 {
4878 int w, h;
4879 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
4880 }
4881
5d7836c4 4882 // Add a new line
1e967276 4883 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 4884
1e967276
JS
4885 // Set relative range so we won't have to change line ranges when paragraphs are moved
4886 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
4887 line->SetPosition(currentPosition);
4888 line->SetSize(wxSize(currentWidth, lineHeight));
4889 line->SetDescent(maxDescent);
4890
603f702b
JS
4891 maxHeight = currentPosition.y + lineHeight;
4892
5d7836c4
JS
4893 // Now move down a line. TODO: add margins, spacing
4894 currentPosition.y += lineHeight;
4895 currentPosition.y += lineSpacing;
5d7836c4 4896 maxDescent = 0;
476a319a 4897 maxAscent = 0;
603f702b
JS
4898 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4899 currentWidth = 0;
7fe8059f 4900
5d7836c4
JS
4901 lineCount ++;
4902
a70eb13e 4903 // TODO: account for zero-length objects
603f702b 4904 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 4905
5d7836c4
JS
4906 lastEndPos = wrapPosition;
4907 lastCompletedEndPos = lastEndPos;
4908
4909 lineHeight = 0;
4910
603f702b
JS
4911 if (wrapPosition < GetRange().GetEnd()-1)
4912 {
4913 // May need to set the node back to a previous one, due to searching back in wrapping
4914 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
4915 if (childAfterWrapPosition)
4916 node = m_children.Find(childAfterWrapPosition);
4917 else
4918 node = node->GetNext();
4919 }
5d7836c4
JS
4920 else
4921 node = node->GetNext();
603f702b
JS
4922
4923 // Apply paragraph styles such as alignment to the wrapped line
4924 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
4925 }
4926 else
4927 {
4928 // We still fit, so don't add a line, and keep going
4929 currentWidth += childSize.x;
a70eb13e
JS
4930
4931 if (childDescent == 0)
4932 {
4933 // An object with a zero descend value wants to take up the whole
4934 // height regardless of baseline
4935 lineHeight = wxMax(lineHeight, childSize.y);
4936 }
4937 else
4938 {
4939 maxDescent = wxMax(childDescent, maxDescent);
4940 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
4941 }
4942
4943 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5d7836c4 4944
603f702b 4945 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
4946 lastEndPos = child->GetRange().GetEnd();
4947
4948 node = node->GetNext();
4949 }
4950 }
4951
07d4142f 4952 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 4953
1e967276
JS
4954 // Remove remaining unused line objects, if any
4955 ClearUnusedLines(lineCount);
4956
603f702b
JS
4957 // We need to add back the margins etc.
4958 {
4959 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4960 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 4961 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4962 SetCachedSize(marginRect.GetSize());
4963 }
4964
4965 // The maximum size is the length of the paragraph stretched out into a line.
4966 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4967 // this size. TODO: take into account line breaks.
4968 {
4969 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 4970 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 4971 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4972 SetMaxSize(marginRect.GetSize());
4973 }
4974
4975 // Find the greatest minimum size. Currently we only look at non-text objects,
4976 // which isn't ideal but it would be slow to find the maximum word width to
4977 // use as the minimum.
4978 {
4979 int minWidth = 0;
4980 node = m_children.GetFirst();
4981 while (node)
4982 {
4983 wxRichTextObject* child = node->GetData();
4984
4985 // If floating, ignore. We already laid out floats.
4986 // Also ignore if empty object, except if we haven't got any
4987 // size yet.
4988 if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
4989 {
4990 if (child->GetCachedSize().x > minWidth)
4991 minWidth = child->GetMinSize().x;
4992 }
4993 node = node->GetNext();
4994 }
5d7836c4 4995
603f702b
JS
4996 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4997 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 4998 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4999 SetMinSize(marginRect.GetSize());
5000 }
5d7836c4 5001
2f45f554
JS
5002#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5003#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5004 // Use the text extents to calculate the size of each fragment in each line
5005 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5006 while (lineNode)
5007 {
5008 wxRichTextLine* line = lineNode->GetData();
5009 wxRichTextRange lineRange = line->GetAbsoluteRange();
5010
5011 // Loop through objects until we get to the one within range
5012 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5013
5014 while (node2)
5015 {
5016 wxRichTextObject* child = node2->GetData();
5017
affbfa1f 5018 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
5019 {
5020 wxRichTextRange rangeToUse = lineRange;
5021 rangeToUse.LimitTo(child->GetRange());
5022
5023 // Find the size of the child from the text extents, and store in an array
5024 // for drawing later
5025 int left = 0;
5026 if (rangeToUse.GetStart() > GetRange().GetStart())
5027 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5028 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5029 int sz = right - left;
5030 line->GetObjectSizes().Add(sz);
5031 }
5032 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5033 // Can break out of inner loop now since we've passed this line's range
5034 break;
5035
5036 node2 = node2->GetNext();
5037 }
5038
5039 lineNode = lineNode->GetNext();
5040 }
5041#endif
5042#endif
5043
5d7836c4
JS
5044 return true;
5045}
5046
603f702b
JS
5047/// Apply paragraph styles, such as centering, to wrapped lines
5048/// TODO: take into account box attributes, possibly
5049void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5050{
5051 if (!attr.HasAlignment())
5052 return;
5053
5054 wxPoint pos = line->GetPosition();
5055 wxSize size = line->GetSize();
5056
5057 // centering, right-justification
8db2e3ef 5058 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5059 {
5060 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5061 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5062 line->SetPosition(pos);
5063 }
8db2e3ef 5064 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5065 {
5066 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5067 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5068 line->SetPosition(pos);
5069 }
5070}
5d7836c4
JS
5071
5072/// Insert text at the given position
5073bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5074{
5075 wxRichTextObject* childToUse = NULL;
09f14108 5076 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5077
5078 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5079 while (node)
5080 {
5081 wxRichTextObject* child = node->GetData();
5082 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5083 {
5084 childToUse = child;
5085 nodeToUse = node;
5086 break;
5087 }
5088
5089 node = node->GetNext();
5090 }
5091
5092 if (childToUse)
5093 {
5094 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5095 if (textObject)
5096 {
5097 int posInString = pos - textObject->GetRange().GetStart();
5098
5099 wxString newText = textObject->GetText().Mid(0, posInString) +
5100 text + textObject->GetText().Mid(posInString);
5101 textObject->SetText(newText);
5102
28f92d74 5103 int textLength = text.length();
5d7836c4
JS
5104
5105 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5106 textObject->GetRange().GetEnd() + textLength));
5107
5108 // Increment the end range of subsequent fragments in this paragraph.
5109 // We'll set the paragraph range itself at a higher level.
5110
5111 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5112 while (node)
5113 {
5114 wxRichTextObject* child = node->GetData();
5115 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5116 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5117
5d7836c4
JS
5118 node = node->GetNext();
5119 }
5120
5121 return true;
5122 }
5123 else
5124 {
5125 // TODO: if not a text object, insert at closest position, e.g. in front of it
5126 }
5127 }
5128 else
5129 {
5130 // Add at end.
5131 // Don't pass parent initially to suppress auto-setting of parent range.
5132 // We'll do that at a higher level.
5133 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5134
5135 AppendChild(textObject);
5136 return true;
5137 }
5138
5139 return false;
5140}
5141
5142void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5143{
bec80f4f 5144 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5145}
5146
5147/// Clear the cached lines
5148void wxRichTextParagraph::ClearLines()
5149{
5150 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5151}
5152
5153/// Get/set the object size for the given range. Returns false if the range
5154/// is invalid for this object.
8db2e3ef 5155bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
5156{
5157 if (!range.IsWithin(GetRange()))
5158 return false;
5159
5160 if (flags & wxRICHTEXT_UNFORMATTED)
5161 {
5162 // Just use unformatted data, assume no line breaks
5d7836c4
JS
5163 wxSize sz;
5164
31778480
JS
5165 wxArrayInt childExtents;
5166 wxArrayInt* p;
5167 if (partialExtents)
5168 p = & childExtents;
5169 else
5170 p = NULL;
5171
a70eb13e
JS
5172 int maxDescent = 0;
5173 int maxAscent = 0;
5174 int maxLineHeight = 0;
5175
5d7836c4
JS
5176 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5177 while (node)
5178 {
5179 wxRichTextObject* child = node->GetData();
5180 if (!child->GetRange().IsOutside(range))
5181 {
cdaed652
VZ
5182 // Floating objects have a zero size within the paragraph.
5183 if (child->IsFloating())
5184 {
5185 if (partialExtents)
5186 {
5187 int lastSize;
5188 if (partialExtents->GetCount() > 0)
5189 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5190 else
5191 lastSize = 0;
5192
5193 partialExtents->Add(0 /* zero size */ + lastSize);
5194 }
5195 }
5196 else
5197 {
603f702b 5198 wxSize childSize;
4f3d5bc0 5199
603f702b
JS
5200 wxRichTextRange rangeToUse = range;
5201 rangeToUse.LimitTo(child->GetRange());
603f702b 5202 int childDescent = 0;
31778480 5203
7c9fdebe 5204 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
603f702b
JS
5205 // but it's only going to be used after caching has taken place.
5206 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5207 {
603f702b
JS
5208 childDescent = child->GetDescent();
5209 childSize = child->GetCachedSize();
2f45f554 5210
a70eb13e
JS
5211 if (childDescent == 0)
5212 {
5213 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5214 }
5215 else
5216 {
5217 maxDescent = wxMax(maxDescent, childDescent);
5218 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5219 }
5220
5221 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5222
5223 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5224 sz.x += childSize.x;
a70eb13e 5225 descent = maxDescent;
603f702b
JS
5226 }
5227 else if (child->IsTopLevel())
31778480 5228 {
603f702b
JS
5229 childDescent = child->GetDescent();
5230 childSize = child->GetCachedSize();
31778480 5231
a70eb13e
JS
5232 if (childDescent == 0)
5233 {
5234 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5235 }
5236 else
5237 {
5238 maxDescent = wxMax(maxDescent, childDescent);
5239 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5240 }
5241
5242 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5243
5244 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5245 sz.x += childSize.x;
a70eb13e
JS
5246 descent = maxDescent;
5247
5248 // FIXME: this won't change the original values.
5249 // Should we be calling GetRangeSize above instead of using cached values?
5250#if 0
603f702b 5251 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5252 {
603f702b
JS
5253 child->SetCachedSize(childSize);
5254 child->SetDescent(childDescent);
31778480 5255 }
a70eb13e 5256#endif
31778480 5257
603f702b
JS
5258 if (partialExtents)
5259 {
5260 int lastSize;
5261 if (partialExtents->GetCount() > 0)
5262 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5263 else
5264 lastSize = 0;
5265
5266 partialExtents->Add(childSize.x + lastSize);
5267 }
5268 }
8db2e3ef 5269 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b 5270 {
a70eb13e
JS
5271 if (childDescent == 0)
5272 {
5273 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5274 }
5275 else
5276 {
5277 maxDescent = wxMax(maxDescent, childDescent);
5278 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5279 }
5280
5281 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5282
5283 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5284 sz.x += childSize.x;
a70eb13e 5285 descent = maxDescent;
603f702b
JS
5286
5287 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5288 {
5289 child->SetCachedSize(childSize);
5290 child->SetDescent(childDescent);
5291 }
5292
5293 if (partialExtents)
5294 {
5295 int lastSize;
5296 if (partialExtents->GetCount() > 0)
5297 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5298 else
5299 lastSize = 0;
5300
5301 size_t i;
5302 for (i = 0; i < childExtents.GetCount(); i++)
5303 {
5304 partialExtents->Add(childExtents[i] + lastSize);
5305 }
5306 }
5307 }
5308 }
5309
5310 if (p)
5311 p->Clear();
5d7836c4
JS
5312 }
5313
5314 node = node->GetNext();
5315 }
5316 size = sz;
5317 }
5318 else
5319 {
5320 // Use formatted data, with line breaks
5321 wxSize sz;
5322
5323 // We're going to loop through each line, and then for each line,
5324 // call GetRangeSize for the fragment that comprises that line.
5325 // Only we have to do that multiple times within the line, because
5326 // the line may be broken into pieces. For now ignore line break commands
5327 // (so we can assume that getting the unformatted size for a fragment
5328 // within a line is the actual size)
5329
5330 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5331 while (node)
5332 {
5333 wxRichTextLine* line = node->GetData();
1e967276
JS
5334 wxRichTextRange lineRange = line->GetAbsoluteRange();
5335 if (!lineRange.IsOutside(range))
5d7836c4 5336 {
a70eb13e
JS
5337 int maxDescent = 0;
5338 int maxAscent = 0;
5339 int maxLineHeight = 0;
5340 int maxLineWidth = 0;
7fe8059f 5341
5d7836c4
JS
5342 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5343 while (node2)
5344 {
5345 wxRichTextObject* child = node2->GetData();
7fe8059f 5346
cdaed652 5347 if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
5d7836c4 5348 {
1e967276 5349 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5350 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5351 if (child->IsTopLevel())
5352 rangeToUse = child->GetOwnRange();
7fe8059f 5353
5d7836c4
JS
5354 wxSize childSize;
5355 int childDescent = 0;
8db2e3ef 5356 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
5d7836c4 5357 {
a70eb13e
JS
5358 if (childDescent == 0)
5359 {
5360 // Assume that if descent is zero, this child can occupy the full line height
5361 // and does not need space for the line's maximum descent. So we influence
5362 // the overall max line height only.
5363 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5364 }
5365 else
5366 {
5367 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5368 maxDescent = wxMax(maxAscent, childDescent);
5369 }
5370 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5371 maxLineWidth += childSize.x;
5d7836c4 5372 }
5d7836c4 5373 }
7fe8059f 5374
5d7836c4
JS
5375 node2 = node2->GetNext();
5376 }
5377
a70eb13e
JS
5378 descent = wxMax(descent, maxDescent);
5379
5d7836c4 5380 // Increase size by a line (TODO: paragraph spacing)
a70eb13e
JS
5381 sz.y += maxLineHeight;
5382 sz.x = wxMax(sz.x, maxLineWidth);
5d7836c4
JS
5383 }
5384 node = node->GetNext();
5385 }
5386 size = sz;
5387 }
5388 return true;
5389}
5390
5391/// Finds the absolute position and row height for the given character position
8db2e3ef 5392bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5393{
5394 if (index == -1)
5395 {
5396 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5397 if (line)
5398 *height = line->GetSize().y;
5399 else
5400 *height = dc.GetCharHeight();
5401
5402 // -1 means 'the start of the buffer'.
5403 pt = GetPosition();
5404 if (line)
5405 pt = pt + line->GetPosition();
5406
5d7836c4
JS
5407 return true;
5408 }
5409
5410 // The final position in a paragraph is taken to mean the position
5411 // at the start of the next paragraph.
5412 if (index == GetRange().GetEnd())
5413 {
5414 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5415 wxASSERT( parent != NULL );
5416
5417 // Find the height at the next paragraph, if any
5418 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5419 if (line)
5420 {
5421 *height = line->GetSize().y;
5422 pt = line->GetAbsolutePosition();
5423 }
5424 else
5425 {
5426 *height = dc.GetCharHeight();
5427 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5428 pt = wxPoint(indent, GetCachedSize().y);
5429 }
5430
5431 return true;
5432 }
5433
5434 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5435 return false;
5436
5437 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5438 while (node)
5439 {
5440 wxRichTextLine* line = node->GetData();
1e967276
JS
5441 wxRichTextRange lineRange = line->GetAbsoluteRange();
5442 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5443 {
5444 // If this is the last point in the line, and we're forcing the
5445 // returned value to be the start of the next line, do the required
5446 // thing.
1e967276 5447 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5448 {
5449 if (node->GetNext())
5450 {
5451 wxRichTextLine* nextLine = node->GetNext()->GetData();
5452 *height = nextLine->GetSize().y;
5453 pt = nextLine->GetAbsolutePosition();
5454 return true;
5455 }
5456 }
5457
5458 pt.y = line->GetPosition().y + GetPosition().y;
5459
1e967276 5460 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5461 wxSize rangeSize;
5462 int descent = 0;
5463
5464 // We find the size of the line up to this point,
5465 // then we can add this size to the line start position and
5466 // paragraph start position to find the actual position.
5467
8db2e3ef 5468 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5469 {
5470 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5471 *height = line->GetSize().y;
5472
5473 return true;
5474 }
5475
5476 }
5477
5478 node = node->GetNext();
5479 }
5480
5481 return false;
5482}
5483
5484/// Hit-testing: returns a flag indicating hit test details, plus
5485/// information about position
8db2e3ef 5486int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5487{
603f702b
JS
5488 if (!IsShown())
5489 return wxRICHTEXT_HITTEST_NONE;
5490
5491 // If we're in the top-level container, then we can return
5492 // a suitable hit test code even if the point is outside the container area,
5493 // so that we can position the caret sensibly even if we don't
5494 // click on valid content. If we're not at the top-level, and the point
5495 // is not within this paragraph object, then we don't want to stop more
5496 // precise hit-testing from working prematurely, so return immediately.
5497 // NEW STRATEGY: use the parent boundary to test whether we're in the
5498 // right region, not the paragraph, since the paragraph may be positioned
5499 // some way in from where the user clicks.
5500 {
5501 long tmpPos;
5502 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5503 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5504 return wxRICHTEXT_HITTEST_NONE;
5505 }
5506
5507 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5508 while (objNode)
5509 {
5510 wxRichTextObject* child = objNode->GetData();
7c9fdebe
JS
5511 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5512 // and also, if this seems composite but actually is marked as atomic,
5513 // don't recurse.
5514 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5515 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
603f702b
JS
5516 {
5517 {
8db2e3ef 5518 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5519 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5520 return hitTest;
5521 }
5522 }
5523
5524 objNode = objNode->GetNext();
5525 }
5526
5d7836c4
JS
5527 wxPoint paraPos = GetPosition();
5528
5529 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5530 while (node)
5531 {
5532 wxRichTextLine* line = node->GetData();
5533 wxPoint linePos = paraPos + line->GetPosition();
5534 wxSize lineSize = line->GetSize();
1e967276 5535 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5536
62381daa 5537 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5538 {
5539 if (pt.x < linePos.x)
5540 {
1e967276 5541 textPosition = lineRange.GetStart();
603f702b
JS
5542 *obj = FindObjectAtPosition(textPosition);
5543 *contextObj = GetContainer();
f262b25c 5544 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5545 }
5546 else if (pt.x >= (linePos.x + lineSize.x))
5547 {
1e967276 5548 textPosition = lineRange.GetEnd();
603f702b
JS
5549 *obj = FindObjectAtPosition(textPosition);
5550 *contextObj = GetContainer();
f262b25c 5551 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5552 }
5553 else
5554 {
2f45f554
JS
5555#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5556 wxArrayInt partialExtents;
5557
5558 wxSize paraSize;
5559 int paraDescent;
5560
5561 // This calculates the partial text extents
8db2e3ef 5562 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
2f45f554
JS
5563
5564 int lastX = linePos.x;
5565 size_t i;
5566 for (i = 0; i < partialExtents.GetCount(); i++)
5567 {
5568 int nextX = partialExtents[i] + linePos.x;
5569
5570 if (pt.x >= lastX && pt.x <= nextX)
5571 {
5572 textPosition = i + lineRange.GetStart(); // minus 1?
5573
603f702b
JS
5574 *obj = FindObjectAtPosition(textPosition);
5575 *contextObj = GetContainer();
5576
2f45f554
JS
5577 // So now we know it's between i-1 and i.
5578 // Let's see if we can be more precise about
5579 // which side of the position it's on.
5580
cdaed652 5581 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5582 if (pt.x >= midPoint)
5583 return wxRICHTEXT_HITTEST_AFTER;
5584 else
5585 return wxRICHTEXT_HITTEST_BEFORE;
5586 }
5587
5588 lastX = nextX;
5589 }
5590#else
5d7836c4
JS
5591 long i;
5592 int lastX = linePos.x;
1e967276 5593 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5594 {
5595 wxSize childSize;
5596 int descent = 0;
7fe8059f 5597
1e967276 5598 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5599
8db2e3ef 5600 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5601
5602 int nextX = childSize.x + linePos.x;
5603
5604 if (pt.x >= lastX && pt.x <= nextX)
5605 {
5606 textPosition = i;
5607
603f702b
JS
5608 *obj = FindObjectAtPosition(textPosition);
5609 *contextObj = GetContainer();
5610
5d7836c4
JS
5611 // So now we know it's between i-1 and i.
5612 // Let's see if we can be more precise about
5613 // which side of the position it's on.
5614
cdaed652 5615 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5616 if (pt.x >= midPoint)
5617 return wxRICHTEXT_HITTEST_AFTER;
5618 else
5619 return wxRICHTEXT_HITTEST_BEFORE;
5620 }
5621 else
5622 {
5623 lastX = nextX;
5624 }
5625 }
2f45f554 5626#endif
5d7836c4
JS
5627 }
5628 }
7fe8059f 5629
5d7836c4
JS
5630 node = node->GetNext();
5631 }
5632
5633 return wxRICHTEXT_HITTEST_NONE;
5634}
5635
5636/// Split an object at this position if necessary, and return
5637/// the previous object, or NULL if inserting at beginning.
5638wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5639{
5640 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5641 while (node)
5642 {
5643 wxRichTextObject* child = node->GetData();
5644
5645 if (pos == child->GetRange().GetStart())
5646 {
5647 if (previousObject)
4d551ad5
JS
5648 {
5649 if (node->GetPrevious())
5650 *previousObject = node->GetPrevious()->GetData();
5651 else
5652 *previousObject = NULL;
5653 }
5d7836c4
JS
5654
5655 return child;
5656 }
5657
5658 if (child->GetRange().Contains(pos))
5659 {
5660 // This should create a new object, transferring part of
5661 // the content to the old object and the rest to the new object.
5662 wxRichTextObject* newObject = child->DoSplit(pos);
5663
5664 // If we couldn't split this object, just insert in front of it.
5665 if (!newObject)
5666 {
5667 // Maybe this is an empty string, try the next one
5668 // return child;
5669 }
5670 else
5671 {
5672 // Insert the new object after 'child'
5673 if (node->GetNext())
5674 m_children.Insert(node->GetNext(), newObject);
5675 else
5676 m_children.Append(newObject);
5677 newObject->SetParent(this);
5678
5679 if (previousObject)
5680 *previousObject = child;
5681
5682 return newObject;
5683 }
5684 }
5685
5686 node = node->GetNext();
5687 }
5688 if (previousObject)
5689 *previousObject = NULL;
5690 return NULL;
5691}
5692
5693/// Move content to a list from obj on
5694void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5695{
5696 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5697 while (node)
5698 {
5699 wxRichTextObject* child = node->GetData();
5700 list.Append(child);
5701
5702 wxRichTextObjectList::compatibility_iterator oldNode = node;
5703
5704 node = node->GetNext();
5705
5706 m_children.DeleteNode(oldNode);
5707 }
5708}
5709
5710/// Add content back from list
5711void wxRichTextParagraph::MoveFromList(wxList& list)
5712{
09f14108 5713 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5714 {
5715 AppendChild((wxRichTextObject*) node->GetData());
5716 }
5717}
5718
5719/// Calculate range
5720void wxRichTextParagraph::CalculateRange(long start, long& end)
5721{
5722 wxRichTextCompositeObject::CalculateRange(start, end);
5723
5724 // Add one for end of paragraph
5725 end ++;
5726
5727 m_range.SetRange(start, end);
5728}
5729
5730/// Find the object at the given position
5731wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5732{
5733 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5734 while (node)
5735 {
5736 wxRichTextObject* obj = node->GetData();
603f702b
JS
5737 if (obj->GetRange().Contains(position) ||
5738 obj->GetRange().GetStart() == position ||
5739 obj->GetRange().GetEnd() == position)
5d7836c4 5740 return obj;
7fe8059f 5741
5d7836c4
JS
5742 node = node->GetNext();
5743 }
5744 return NULL;
5745}
5746
5747/// Get the plain text searching from the start or end of the range.
5748/// The resulting string may be shorter than the range given.
5749bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5750{
5751 text = wxEmptyString;
5752
5753 if (fromStart)
5754 {
5755 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5756 while (node)
5757 {
5758 wxRichTextObject* obj = node->GetData();
5759 if (!obj->GetRange().IsOutside(range))
5760 {
5761 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5762 if (textObj)
5763 {
5764 text += textObj->GetTextForRange(range);
5765 }
5766 else
043c0d58
JS
5767 {
5768 text += wxT(" ");
5769 }
5d7836c4
JS
5770 }
5771
5772 node = node->GetNext();
5773 }
5774 }
5775 else
5776 {
5777 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5778 while (node)
5779 {
5780 wxRichTextObject* obj = node->GetData();
5781 if (!obj->GetRange().IsOutside(range))
5782 {
5783 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5784 if (textObj)
5785 {
5786 text = textObj->GetTextForRange(range) + text;
5787 }
5788 else
043c0d58
JS
5789 {
5790 text = wxT(" ") + text;
5791 }
5d7836c4
JS
5792 }
5793
5794 node = node->GetPrevious();
5795 }
5796 }
5797
5798 return true;
5799}
5800
5801/// Find a suitable wrap position.
8db2e3ef 5802bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5803{
72945e24
JS
5804 if (range.GetLength() <= 0)
5805 return false;
5806
5d7836c4
JS
5807 // Find the first position where the line exceeds the available space.
5808 wxSize sz;
5d7836c4 5809 long breakPosition = range.GetEnd();
ecb5fbf1 5810
31778480
JS
5811#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5812 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 5813 {
31778480 5814 int widthBefore;
5d7836c4 5815
31778480
JS
5816 if (range.GetStart() > GetRange().GetStart())
5817 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5818 else
5819 widthBefore = 0;
5820
5821 size_t i;
43a0d1e1 5822 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 5823 {
31778480 5824 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 5825
72945e24 5826 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 5827 {
31778480
JS
5828 breakPosition = i-1;
5829 break;
ecb5fbf1 5830 }
5d7836c4 5831 }
31778480
JS
5832 }
5833 else
5834#endif
5835 {
5836 // Binary chop for speed
5837 long minPos = range.GetStart();
5838 long maxPos = range.GetEnd();
5839 while (true)
ecb5fbf1 5840 {
31778480
JS
5841 if (minPos == maxPos)
5842 {
5843 int descent = 0;
8db2e3ef 5844 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 5845
31778480
JS
5846 if (sz.x > availableSpace)
5847 breakPosition = minPos - 1;
5848 break;
5849 }
5850 else if ((maxPos - minPos) == 1)
ecb5fbf1 5851 {
31778480 5852 int descent = 0;
8db2e3ef 5853 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5854
5855 if (sz.x > availableSpace)
5856 breakPosition = minPos - 1;
5857 else
5858 {
8db2e3ef 5859 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5860 if (sz.x > availableSpace)
5861 breakPosition = maxPos-1;
5862 }
5863 break;
ecb5fbf1
JS
5864 }
5865 else
5866 {
31778480
JS
5867 long nextPos = minPos + ((maxPos - minPos) / 2);
5868
5869 int descent = 0;
8db2e3ef 5870 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5871
5872 if (sz.x > availableSpace)
5873 {
5874 maxPos = nextPos;
5875 }
5876 else
5877 {
5878 minPos = nextPos;
5879 }
ecb5fbf1
JS
5880 }
5881 }
5d7836c4
JS
5882 }
5883
5884 // Now we know the last position on the line.
5885 // Let's try to find a word break.
5886
5887 wxString plainText;
5888 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
5889 {
ff76711f
JS
5890 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
5891 if (newLinePos != wxNOT_FOUND)
5d7836c4 5892 {
ff76711f
JS
5893 breakPosition = wxMax(0, range.GetStart() + newLinePos);
5894 }
5895 else
5896 {
5897 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
5898 int tabPos = plainText.Find(wxT('\t'), true);
5899 int pos = wxMax(spacePos, tabPos);
5900 if (pos != wxNOT_FOUND)
ff76711f 5901 {
31002e44 5902 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
5903 breakPosition = breakPosition - positionsFromEndOfString;
5904 }
5d7836c4
JS
5905 }
5906 }
5907
5908 wrapPosition = breakPosition;
5909
5910 return true;
5911}
5912
5913/// Get the bullet text for this paragraph.
5914wxString wxRichTextParagraph::GetBulletText()
5915{
5916 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
5917 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
5918 return wxEmptyString;
5919
5920 int number = GetAttributes().GetBulletNumber();
5921
5922 wxString text;
d2d0adc7 5923 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
5924 {
5925 text.Printf(wxT("%d"), number);
5926 }
5927 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
5928 {
5929 // TODO: Unicode, and also check if number > 26
5930 text.Printf(wxT("%c"), (wxChar) (number+64));
5931 }
5932 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
5933 {
5934 // TODO: Unicode, and also check if number > 26
5935 text.Printf(wxT("%c"), (wxChar) (number+96));
5936 }
5937 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
5938 {
59509217 5939 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
5940 }
5941 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
5942 {
59509217
JS
5943 text = wxRichTextDecimalToRoman(number);
5944 text.MakeLower();
5d7836c4
JS
5945 }
5946 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
5947 {
d2d0adc7
JS
5948 text = GetAttributes().GetBulletText();
5949 }
3e541562 5950
d2d0adc7
JS
5951 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
5952 {
5953 // The outline style relies on the text being computed statically,
5954 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5955 // should be stored in the attributes; if not, just use the number for this
5956 // level, as previously computed.
5957 if (!GetAttributes().GetBulletText().IsEmpty())
5958 text = GetAttributes().GetBulletText();
5d7836c4
JS
5959 }
5960
5961 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
5962 {
5963 text = wxT("(") + text + wxT(")");
5964 }
d2d0adc7
JS
5965 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
5966 {
5967 text = text + wxT(")");
5968 }
5969
5d7836c4
JS
5970 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
5971 {
5972 text += wxT(".");
5973 }
5974
5975 return text;
5976}
5977
1e967276
JS
5978/// Allocate or reuse a line object
5979wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
5980{
5981 if (pos < (int) m_cachedLines.GetCount())
5982 {
5983 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
5984 line->Init(this);
5985 return line;
5986 }
5987 else
5988 {
5989 wxRichTextLine* line = new wxRichTextLine(this);
5990 m_cachedLines.Append(line);
5991 return line;
5992 }
5993}
5994
5995/// Clear remaining unused line objects, if any
5996bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
5997{
5998 int cachedLineCount = m_cachedLines.GetCount();
5999 if ((int) cachedLineCount > lineCount)
6000 {
6001 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6002 {
6003 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6004 wxRichTextLine* line = node->GetData();
6005 m_cachedLines.Erase(node);
6006 delete line;
6007 }
6008 }
6009 return true;
6010}
6011
fe5aa22c
JS
6012/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6013/// retrieve the actual style.
603f702b 6014wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 6015{
24777478 6016 wxRichTextAttr attr;
603f702b 6017 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6018 if (buf)
6019 {
6020 attr = buf->GetBasicStyle();
603f702b
JS
6021 if (!includingBoxAttr)
6022 {
6023 attr.GetTextBoxAttr().Reset();
6024 // The background colour will be painted by the container, and we don't
6025 // want to unnecessarily overwrite the background when we're drawing text
6026 // because this may erase the guideline (which appears just under the text
6027 // if there's no padding).
6028 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6029 }
fe5aa22c
JS
6030 wxRichTextApplyStyle(attr, GetAttributes());
6031 }
6032 else
6033 attr = GetAttributes();
6034
6035 wxRichTextApplyStyle(attr, contentStyle);
6036 return attr;
6037}
6038
6039/// Get combined attributes of the base style and paragraph style.
603f702b 6040wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 6041{
24777478 6042 wxRichTextAttr attr;
603f702b 6043 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6044 if (buf)
6045 {
6046 attr = buf->GetBasicStyle();
603f702b
JS
6047 if (!includingBoxAttr)
6048 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
6049 wxRichTextApplyStyle(attr, GetAttributes());
6050 }
6051 else
6052 attr = GetAttributes();
6053
6054 return attr;
6055}
5d7836c4 6056
603f702b 6057// Create default tabstop array
cfa3b256
JS
6058void wxRichTextParagraph::InitDefaultTabs()
6059{
6060 // create a default tab list at 10 mm each.
6061 for (int i = 0; i < 20; ++i)
6062 {
6063 sm_defaultTabs.Add(i*100);
6064 }
6065}
6066
603f702b 6067// Clear default tabstop array
cfa3b256
JS
6068void wxRichTextParagraph::ClearDefaultTabs()
6069{
6070 sm_defaultTabs.Clear();
6071}
6072
8db2e3ef 6073void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
6074{
6075 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6076 while (node)
6077 {
bec80f4f 6078 wxRichTextObject* anchored = node->GetData();
07d4142f 6079 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652
VZ
6080 {
6081 wxSize size;
6082 int descent, x = 0;
8db2e3ef 6083 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
bec80f4f 6084
24777478 6085 int offsetY = 0;
603f702b 6086 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
6087 {
6088 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6089 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6090 {
6091 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6092 }
6093 }
bec80f4f 6094
24777478 6095 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6096
cdaed652 6097 /* Update the offset */
24777478
JS
6098 int newOffsetY = pos - rect.y;
6099 if (newOffsetY != offsetY)
6100 {
6101 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6102 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6103 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6104 }
cdaed652 6105
24777478 6106 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6107 x = rect.x;
24777478 6108 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6109 x = rect.x + rect.width - size.x;
24777478 6110
cdaed652
VZ
6111 anchored->SetPosition(wxPoint(x, pos));
6112 anchored->SetCachedSize(size);
6113 floatCollector->CollectFloat(this, anchored);
6114 }
6115
6116 node = node->GetNext();
6117 }
6118}
6119
603f702b 6120// Get the first position from pos that has a line break character.
ff76711f
JS
6121long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6122{
6123 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6124 while (node)
6125 {
6126 wxRichTextObject* obj = node->GetData();
6127 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6128 {
6129 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6130 if (textObj)
6131 {
6132 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6133 if (breakPos > -1)
6134 return breakPos;
6135 }
6136 }
6137 node = node->GetNext();
6138 }
6139 return -1;
6140}
cfa3b256 6141
5d7836c4
JS
6142/*!
6143 * wxRichTextLine
6144 * This object represents a line in a paragraph, and stores
6145 * offsets from the start of the paragraph representing the
6146 * start and end positions of the line.
6147 */
6148
6149wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6150{
1e967276 6151 Init(parent);
5d7836c4
JS
6152}
6153
6154/// Initialisation
1e967276 6155void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6156{
1e967276
JS
6157 m_parent = parent;
6158 m_range.SetRange(-1, -1);
6159 m_pos = wxPoint(0, 0);
6160 m_size = wxSize(0, 0);
5d7836c4 6161 m_descent = 0;
2f45f554
JS
6162#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6163 m_objectSizes.Clear();
6164#endif
5d7836c4
JS
6165}
6166
6167/// Copy
6168void wxRichTextLine::Copy(const wxRichTextLine& obj)
6169{
6170 m_range = obj.m_range;
2f45f554
JS
6171#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6172 m_objectSizes = obj.m_objectSizes;
6173#endif
5d7836c4
JS
6174}
6175
6176/// Get the absolute object position
6177wxPoint wxRichTextLine::GetAbsolutePosition() const
6178{
6179 return m_parent->GetPosition() + m_pos;
6180}
6181
1e967276
JS
6182/// Get the absolute range
6183wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6184{
6185 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6186 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6187 return range;
6188}
6189
5d7836c4
JS
6190/*!
6191 * wxRichTextPlainText
6192 * This object represents a single piece of text.
6193 */
6194
6195IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6196
24777478 6197wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6198 wxRichTextObject(parent)
6199{
5d7836c4
JS
6200 if (style)
6201 SetAttributes(*style);
6202
6203 m_text = text;
6204}
6205
cfa3b256
JS
6206#define USE_KERNING_FIX 1
6207
4794d69c
JS
6208// If insufficient tabs are defined, this is the tab width used
6209#define WIDTH_FOR_DEFAULT_TABS 50
6210
5d7836c4 6211/// Draw the item
8db2e3ef 6212bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6213{
fe5aa22c
JS
6214 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6215 wxASSERT (para != NULL);
6216
603f702b 6217 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6218 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6219
6220 // Let's make the assumption for now that for content in a paragraph, including
6221 // text, we never have a discontinuous selection. So we only deal with a
6222 // single range.
6223 wxRichTextRange selectionRange;
6224 if (selection.IsValid())
6225 {
6226 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6227 if (selectionRanges.GetCount() > 0)
6228 selectionRange = selectionRanges[0];
6229 else
6230 selectionRange = wxRICHTEXT_NO_SELECTION;
6231 }
6232 else
6233 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6234
5d7836c4
JS
6235 int offset = GetRange().GetStart();
6236
ff76711f
JS
6237 // Replace line break characters with spaces
6238 wxString str = m_text;
6239 wxString toRemove = wxRichTextLineBreakChar;
6240 str.Replace(toRemove, wxT(" "));
c025e094
JS
6241 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6242 str.MakeUpper();
3e541562 6243
5d7836c4 6244 long len = range.GetLength();
ff76711f 6245 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6246
5d7836c4
JS
6247 // Test for the optimized situations where all is selected, or none
6248 // is selected.
6249
30bf7630
JS
6250 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6251 wxCheckSetFont(dc, textFont);
6252 int charHeight = dc.GetCharHeight();
6253
6254 int x, y;
a1b806b9 6255 if ( textFont.IsOk() )
30bf7630
JS
6256 {
6257 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6258 {
6259 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6260 textFont.SetPointSize( static_cast<int>(size) );
6261 x = rect.x;
6262 y = rect.y;
6263 wxCheckSetFont(dc, textFont);
6264 }
6265 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6266 {
6267 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6268 textFont.SetPointSize( static_cast<int>(size) );
6269 x = rect.x;
6270 int sub_height = static_cast<int>( static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6271 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6272 wxCheckSetFont(dc, textFont);
6273 }
6274 else
6275 {
6276 x = rect.x;
6277 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6278 }
6279 }
6280 else
6281 {
6282 x = rect.x;
6283 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6284 }
5d7836c4 6285
603f702b
JS
6286 // TODO: new selection code
6287
5d7836c4
JS
6288 // (a) All selected.
6289 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6290 {
fe5aa22c 6291 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6292 }
6293 // (b) None selected.
6294 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6295 {
6296 // Draw all unselected
fe5aa22c 6297 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6298 }
6299 else
6300 {
6301 // (c) Part selected, part not
6302 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6303
04ee05f9 6304 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6305
5d7836c4
JS
6306 // 1. Initial unselected chunk, if any, up until start of selection.
6307 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6308 {
6309 int r1 = range.GetStart();
6310 int s1 = selectionRange.GetStart()-1;
6311 int fragmentLen = s1 - r1 + 1;
6312 if (fragmentLen < 0)
af588446 6313 {
5d7836c4 6314 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6315 }
ff76711f 6316 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6317
fe5aa22c 6318 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6319
6320#if USE_KERNING_FIX
6321 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6322 {
6323 // Compensate for kerning difference
ff76711f
JS
6324 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6325 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6326
cfa3b256
JS
6327 wxCoord w1, h1, w2, h2, w3, h3;
6328 dc.GetTextExtent(stringFragment, & w1, & h1);
6329 dc.GetTextExtent(stringFragment2, & w2, & h2);
6330 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6331
cfa3b256
JS
6332 int kerningDiff = (w1 + w3) - w2;
6333 x = x - kerningDiff;
6334 }
6335#endif
5d7836c4
JS
6336 }
6337
6338 // 2. Selected chunk, if any.
6339 if (selectionRange.GetEnd() >= range.GetStart())
6340 {
6341 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6342 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6343
6344 int fragmentLen = s2 - s1 + 1;
6345 if (fragmentLen < 0)
af588446 6346 {
5d7836c4 6347 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6348 }
ff76711f 6349 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6350
fe5aa22c 6351 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6352
6353#if USE_KERNING_FIX
6354 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6355 {
6356 // Compensate for kerning difference
ff76711f
JS
6357 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6358 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6359
cfa3b256
JS
6360 wxCoord w1, h1, w2, h2, w3, h3;
6361 dc.GetTextExtent(stringFragment, & w1, & h1);
6362 dc.GetTextExtent(stringFragment2, & w2, & h2);
6363 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6364
cfa3b256
JS
6365 int kerningDiff = (w1 + w3) - w2;
6366 x = x - kerningDiff;
6367 }
6368#endif
5d7836c4
JS
6369 }
6370
6371 // 3. Remaining unselected chunk, if any
6372 if (selectionRange.GetEnd() < range.GetEnd())
6373 {
6374 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6375 int r2 = range.GetEnd();
6376
6377 int fragmentLen = r2 - s2 + 1;
6378 if (fragmentLen < 0)
af588446 6379 {
5d7836c4 6380 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6381 }
ff76711f 6382 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6383
fe5aa22c 6384 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6385 }
5d7836c4
JS
6386 }
6387
6388 return true;
6389}
61399247 6390
24777478 6391bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6392{
cfa3b256
JS
6393 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6394
6395 wxArrayInt tabArray;
6396 int tabCount;
6397 if (hasTabs)
ab14c7aa 6398 {
cfa3b256
JS
6399 if (attr.GetTabs().IsEmpty())
6400 tabArray = wxRichTextParagraph::GetDefaultTabs();
6401 else
6402 tabArray = attr.GetTabs();
6403 tabCount = tabArray.GetCount();
6404
6405 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6406 {
cfa3b256
JS
6407 int pos = tabArray[i];
6408 pos = ConvertTenthsMMToPixels(dc, pos);
6409 tabArray[i] = pos;
7f0d9d71
JS
6410 }
6411 }
cfa3b256
JS
6412 else
6413 tabCount = 0;
ab14c7aa 6414
cfa3b256
JS
6415 int nextTabPos = -1;
6416 int tabPos = -1;
7f0d9d71 6417 wxCoord w, h;
ab14c7aa 6418
cfa3b256 6419 if (selected)
ab14c7aa 6420 {
0ec6da02
JS
6421 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6422 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6423
ecb5fbf1
JS
6424 wxCheckSetBrush(dc, wxBrush(highlightColour));
6425 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6426 dc.SetTextForeground(highlightTextColour);
04ee05f9 6427 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6428 }
ab14c7aa
JS
6429 else
6430 {
fe5aa22c 6431 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6432
f0e9eda2
JS
6433 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6434 {
04ee05f9 6435 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6436 dc.SetTextBackground(attr.GetBackgroundColour());
6437 }
6438 else
04ee05f9 6439 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6440 }
3e541562 6441
925a662a 6442 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6443 while (hasTabs)
ab14c7aa
JS
6444 {
6445 // the string has a tab
7f0d9d71
JS
6446 // break up the string at the Tab
6447 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6448 str = str.AfterFirst(wxT('\t'));
6449 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6450 tabPos = x + w;
7f0d9d71 6451 bool not_found = true;
cfa3b256 6452 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6453 {
015d0446 6454 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6455
6456 // Find the next tab position.
6457 // Even if we're at the end of the tab array, we must still draw the chunk.
6458
6459 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6460 {
4794d69c
JS
6461 if (nextTabPos <= tabPos)
6462 {
6463 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6464 nextTabPos = tabPos + defaultTabWidth;
6465 }
6466
7f0d9d71 6467 not_found = false;
ab14c7aa
JS
6468 if (selected)
6469 {
cfa3b256 6470 w = nextTabPos - x;
7f0d9d71 6471 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6472 dc.DrawRectangle(selRect);
7f0d9d71
JS
6473 }
6474 dc.DrawText(stringChunk, x, y);
42688aea
JS
6475
6476 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6477 {
6478 wxPen oldPen = dc.GetPen();
ecb5fbf1 6479 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6480 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6481 wxCheckSetPen(dc, oldPen);
42688aea
JS
6482 }
6483
cfa3b256 6484 x = nextTabPos;
7f0d9d71
JS
6485 }
6486 }
cfa3b256 6487 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6488 }
61399247 6489
cfa3b256 6490 if (!str.IsEmpty())
ab14c7aa 6491 {
cfa3b256
JS
6492 dc.GetTextExtent(str, & w, & h);
6493 if (selected)
6494 {
6495 wxRect selRect(x, rect.y, w, rect.GetHeight());
6496 dc.DrawRectangle(selRect);
6497 }
6498 dc.DrawText(str, x, y);
42688aea
JS
6499
6500 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6501 {
6502 wxPen oldPen = dc.GetPen();
ecb5fbf1 6503 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6504 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6505 wxCheckSetPen(dc, oldPen);
42688aea
JS
6506 }
6507
cfa3b256 6508 x += w;
7f0d9d71 6509 }
5d7836c4 6510
7c9fdebe 6511 return true;
7f0d9d71 6512}
fe5aa22c 6513
5d7836c4 6514/// Lay the item out
8db2e3ef 6515bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6516{
ecb5fbf1
JS
6517 // Only lay out if we haven't already cached the size
6518 if (m_size.x == -1)
8db2e3ef 6519 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6520 m_maxSize = m_size;
6521 // Eventually we want to have a reasonable estimate of minimum size.
6522 m_minSize = wxSize(0, 0);
5d7836c4
JS
6523 return true;
6524}
6525
6526/// Copy
6527void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6528{
6529 wxRichTextObject::Copy(obj);
6530
6531 m_text = obj.m_text;
6532}
6533
6534/// Get/set the object size for the given range. Returns false if the range
6535/// is invalid for this object.
8db2e3ef 6536bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
6537{
6538 if (!range.IsWithin(GetRange()))
6539 return false;
6540
fe5aa22c
JS
6541 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6542 wxASSERT (para != NULL);
603f702b 6543
925a662a 6544 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6545
24777478 6546 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6547 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6548
5d7836c4
JS
6549 // Always assume unformatted text, since at this level we have no knowledge
6550 // of line breaks - and we don't need it, since we'll calculate size within
6551 // formatted text by doing it in chunks according to the line ranges
6552
30bf7630 6553 bool bScript(false);
44cc96a8 6554 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6555 if (font.IsOk())
30bf7630
JS
6556 {
6557 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6558 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6559 {
6560 wxFont textFont = font;
6561 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6562 textFont.SetPointSize( static_cast<int>(size) );
6563 wxCheckSetFont(dc, textFont);
6564 bScript = true;
6565 }
6566 else
6567 {
6568 wxCheckSetFont(dc, font);
6569 }
6570 }
5d7836c4 6571
109bfc88 6572 bool haveDescent = false;
5d7836c4
JS
6573 int startPos = range.GetStart() - GetRange().GetStart();
6574 long len = range.GetLength();
3e541562 6575
ff76711f
JS
6576 wxString str(m_text);
6577 wxString toReplace = wxRichTextLineBreakChar;
6578 str.Replace(toReplace, wxT(" "));
6579
6580 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea
JS
6581
6582 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6583 stringChunk.MakeUpper();
6584
5d7836c4 6585 wxCoord w, h;
7f0d9d71 6586 int width = 0;
cfa3b256 6587 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6588 {
6589 // the string has a tab
cfa3b256
JS
6590 wxArrayInt tabArray;
6591 if (textAttr.GetTabs().IsEmpty())
6592 tabArray = wxRichTextParagraph::GetDefaultTabs();
6593 else
6594 tabArray = textAttr.GetTabs();
ab14c7aa 6595
cfa3b256 6596 int tabCount = tabArray.GetCount();
41a85215 6597
cfa3b256 6598 for (int i = 0; i < tabCount; ++i)
61399247 6599 {
cfa3b256
JS
6600 int pos = tabArray[i];
6601 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6602 tabArray[i] = pos;
7f0d9d71 6603 }
41a85215 6604
cfa3b256 6605 int nextTabPos = -1;
61399247 6606
ab14c7aa
JS
6607 while (stringChunk.Find(wxT('\t')) >= 0)
6608 {
109bfc88
JS
6609 int absoluteWidth = 0;
6610
ab14c7aa 6611 // the string has a tab
7f0d9d71
JS
6612 // break up the string at the Tab
6613 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6614 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6615
31778480
JS
6616 if (partialExtents)
6617 {
109bfc88
JS
6618 int oldWidth;
6619 if (partialExtents->GetCount() > 0)
6620 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6621 else
6622 oldWidth = 0;
6623
31778480
JS
6624 // Add these partial extents
6625 wxArrayInt p;
6626 dc.GetPartialTextExtents(stringFragment, p);
6627 size_t j;
6628 for (j = 0; j < p.GetCount(); j++)
6629 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6630
6631 if (partialExtents->GetCount() > 0)
925a662a 6632 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6633 else
925a662a 6634 absoluteWidth = relativeX;
109bfc88
JS
6635 }
6636 else
6637 {
6638 dc.GetTextExtent(stringFragment, & w, & h);
6639 width += w;
603f702b 6640 absoluteWidth = width + relativeX;
109bfc88 6641 haveDescent = true;
31778480
JS
6642 }
6643
cfa3b256
JS
6644 bool notFound = true;
6645 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6646 {
cfa3b256 6647 nextTabPos = tabArray.Item(i);
4794d69c
JS
6648
6649 // Find the next tab position.
6650 // Even if we're at the end of the tab array, we must still process the chunk.
6651
6652 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6653 {
4794d69c
JS
6654 if (nextTabPos <= absoluteWidth)
6655 {
6656 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6657 nextTabPos = absoluteWidth + defaultTabWidth;
6658 }
6659
cfa3b256 6660 notFound = false;
925a662a 6661 width = nextTabPos - relativeX;
31778480
JS
6662
6663 if (partialExtents)
6664 partialExtents->Add(width);
7f0d9d71
JS
6665 }
6666 }
6667 }
6668 }
30bf7630 6669
31778480
JS
6670 if (!stringChunk.IsEmpty())
6671 {
31778480
JS
6672 if (partialExtents)
6673 {
109bfc88
JS
6674 int oldWidth;
6675 if (partialExtents->GetCount() > 0)
6676 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6677 else
6678 oldWidth = 0;
6679
31778480
JS
6680 // Add these partial extents
6681 wxArrayInt p;
6682 dc.GetPartialTextExtents(stringChunk, p);
6683 size_t j;
6684 for (j = 0; j < p.GetCount(); j++)
6685 partialExtents->Add(oldWidth + p[j]);
6686 }
109bfc88
JS
6687 else
6688 {
6689 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6690 width += w;
6691 haveDescent = true;
6692 }
6693 }
6694
6695 if (partialExtents)
6696 {
6697 int charHeight = dc.GetCharHeight();
6698 if ((*partialExtents).GetCount() > 0)
6699 w = (*partialExtents)[partialExtents->GetCount()-1];
6700 else
6701 w = 0;
6702 size = wxSize(w, charHeight);
6703 }
6704 else
6705 {
6706 size = wxSize(width, dc.GetCharHeight());
31778480 6707 }
30bf7630 6708
109bfc88
JS
6709 if (!haveDescent)
6710 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6711
30bf7630
JS
6712 if ( bScript )
6713 dc.SetFont(font);
6714
5d7836c4
JS
6715 return true;
6716}
6717
6718/// Do a split, returning an object containing the second part, and setting
6719/// the first part in 'this'.
6720wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6721{
ff76711f 6722 long index = pos - GetRange().GetStart();
3e541562 6723
28f92d74 6724 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6725 return NULL;
6726
6727 wxString firstPart = m_text.Mid(0, index);
6728 wxString secondPart = m_text.Mid(index);
6729
6730 m_text = firstPart;
6731
6732 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6733 newObject->SetAttributes(GetAttributes());
8db2e3ef 6734 newObject->SetProperties(GetProperties());
5d7836c4
JS
6735
6736 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6737 GetRange().SetEnd(pos-1);
3e541562 6738
5d7836c4
JS
6739 return newObject;
6740}
6741
6742/// Calculate range
6743void wxRichTextPlainText::CalculateRange(long start, long& end)
6744{
28f92d74 6745 end = start + m_text.length() - 1;
5d7836c4
JS
6746 m_range.SetRange(start, end);
6747}
6748
6749/// Delete range
6750bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6751{
6752 wxRichTextRange r = range;
6753
6754 r.LimitTo(GetRange());
6755
6756 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6757 {
6758 m_text.Empty();
6759 return true;
6760 }
6761
6762 long startIndex = r.GetStart() - GetRange().GetStart();
6763 long len = r.GetLength();
6764
6765 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6766 return true;
6767}
6768
6769/// Get text for the given range.
6770wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6771{
6772 wxRichTextRange r = range;
6773
6774 r.LimitTo(GetRange());
6775
6776 long startIndex = r.GetStart() - GetRange().GetStart();
6777 long len = r.GetLength();
6778
6779 return m_text.Mid(startIndex, len);
6780}
6781
6782/// Returns true if this object can merge itself with the given one.
6783bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
6784{
6785 return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) &&
8db2e3ef 6786 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
5d7836c4
JS
6787}
6788
6789/// Returns true if this object merged itself with the given one.
6790/// The calling code will then delete the given object.
6791bool wxRichTextPlainText::Merge(wxRichTextObject* object)
6792{
6793 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
6794 wxASSERT( textObject != NULL );
6795
6796 if (textObject)
6797 {
6798 m_text += textObject->GetText();
99404ab0 6799 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
6800 return true;
6801 }
6802 else
6803 return false;
6804}
6805
6806/// Dump to output stream for debugging
6807void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
6808{
6809 wxRichTextObject::Dump(stream);
6810 stream << m_text << wxT("\n");
6811}
6812
ff76711f
JS
6813/// Get the first position from pos that has a line break character.
6814long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
6815{
6816 int i;
6817 int len = m_text.length();
6818 int startPos = pos - m_range.GetStart();
6819 for (i = startPos; i < len; i++)
6820 {
6821 wxChar ch = m_text[i];
6822 if (ch == wxRichTextLineBreakChar)
6823 {
6824 return i + m_range.GetStart();
6825 }
6826 }
6827 return -1;
6828}
6829
5d7836c4
JS
6830/*!
6831 * wxRichTextBuffer
6832 * This is a kind of box, used to represent the whole buffer
6833 */
6834
6835IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
6836
7c9fdebe
JS
6837wxList wxRichTextBuffer::sm_handlers;
6838wxList wxRichTextBuffer::sm_drawingHandlers;
6839wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
6840wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
6841int wxRichTextBuffer::sm_bulletRightMargin = 20;
6842float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
5d7836c4
JS
6843
6844/// Initialisation
6845void wxRichTextBuffer::Init()
6846{
6847 m_commandProcessor = new wxCommandProcessor;
6848 m_styleSheet = NULL;
6849 m_modified = false;
6850 m_batchedCommandDepth = 0;
6851 m_batchedCommand = NULL;
6852 m_suppressUndo = 0;
d2d0adc7 6853 m_handlerFlags = 0;
44219ff0 6854 m_scale = 1.0;
f819ed5d 6855 SetMargins(4);
5d7836c4
JS
6856}
6857
6858/// Initialisation
6859wxRichTextBuffer::~wxRichTextBuffer()
6860{
6861 delete m_commandProcessor;
6862 delete m_batchedCommand;
6863
6864 ClearStyleStack();
d2d0adc7 6865 ClearEventHandlers();
5d7836c4
JS
6866}
6867
85d8909b 6868void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 6869{
85d8909b 6870 Reset();
3e541562 6871
5d7836c4 6872 GetCommandProcessor()->ClearCommands();
5d7836c4 6873
5d7836c4 6874 Modify(false);
1e967276 6875 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
6876}
6877
0ca07313
JS
6878void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
6879{
6880 wxRichTextParagraphLayoutBox::Copy(obj);
6881
6882 m_styleSheet = obj.m_styleSheet;
6883 m_modified = obj.m_modified;
bec80f4f
JS
6884 m_batchedCommandDepth = 0;
6885 if (m_batchedCommand)
6886 delete m_batchedCommand;
6887 m_batchedCommand = NULL;
0ca07313 6888 m_suppressUndo = obj.m_suppressUndo;
603f702b 6889 m_invalidRange = obj.m_invalidRange;
0ca07313
JS
6890}
6891
38f833b1
JS
6892/// Push style sheet to top of stack
6893bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
6894{
6895 if (m_styleSheet)
6896 styleSheet->InsertSheet(m_styleSheet);
6897
6898 SetStyleSheet(styleSheet);
41a85215 6899
38f833b1
JS
6900 return true;
6901}
6902
6903/// Pop style sheet from top of stack
6904wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
6905{
6906 if (m_styleSheet)
6907 {
6908 wxRichTextStyleSheet* oldSheet = m_styleSheet;
6909 m_styleSheet = oldSheet->GetNextSheet();
6910 oldSheet->Unlink();
41a85215 6911
38f833b1
JS
6912 return oldSheet;
6913 }
6914 else
6915 return NULL;
6916}
6917
0ca07313
JS
6918/// Submit command to insert paragraphs
6919bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
6920{
4e63bfb9 6921 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
6922}
6923
6924/// Submit command to insert paragraphs
4e63bfb9 6925bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
6926{
6927 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 6928
0ca07313 6929 action->GetNewParagraphs() = paragraphs;
59509217 6930
0ca07313
JS
6931 action->SetPosition(pos);
6932
603f702b 6933 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
6934 if (!paragraphs.GetPartialParagraph())
6935 range.SetEnd(range.GetEnd()+1);
6936
0ca07313 6937 // Set the range we'll need to delete in Undo
99404ab0 6938 action->SetRange(range);
0ca07313 6939
603f702b 6940 buffer->SubmitAction(action);
0ca07313
JS
6941
6942 return true;
6943}
6944
5d7836c4 6945/// Submit command to insert the given text
fe5aa22c 6946bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 6947{
4e63bfb9 6948 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
6949}
6950
6951/// Submit command to insert the given text
4e63bfb9 6952bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
6953{
6954 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 6955
24777478
JS
6956 wxRichTextAttr* p = NULL;
6957 wxRichTextAttr paraAttr;
fe5aa22c
JS
6958 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6959 {
7c081bd2 6960 // Get appropriate paragraph style
603f702b 6961 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
6962 if (!paraAttr.IsDefault())
6963 p = & paraAttr;
6964 }
6965
fe5aa22c 6966 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 6967
603f702b 6968 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 6969
6636ef8d 6970 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
6971 {
6972 // Don't count the newline when undoing
6973 length --;
5d7836c4 6974 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 6975 }
6636ef8d 6976 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 6977 length --;
5d7836c4
JS
6978
6979 action->SetPosition(pos);
6980
6981 // Set the range we'll need to delete in Undo
0ca07313 6982 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 6983
603f702b 6984 buffer->SubmitAction(action);
7fe8059f 6985
5d7836c4
JS
6986 return true;
6987}
6988
6989/// Submit command to insert the given text
fe5aa22c 6990bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 6991{
4e63bfb9 6992 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
6993}
6994
6995/// Submit command to insert the given text
4e63bfb9 6996bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
6997{
6998 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 6999
24777478
JS
7000 wxRichTextAttr* p = NULL;
7001 wxRichTextAttr paraAttr;
fe5aa22c
JS
7002 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7003 {
603f702b 7004 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
7005 if (!paraAttr.IsDefault())
7006 p = & paraAttr;
7007 }
7008
603f702b 7009 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f
WS
7010
7011 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
7012 action->GetNewParagraphs().AppendChild(newPara);
7013 action->GetNewParagraphs().UpdateRanges();
7014 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
7015 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7016 long pos1 = pos;
7017
6c0ea513
JS
7018 if (p)
7019 newPara->SetAttributes(*p);
7020
c025e094
JS
7021 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7022 {
7023 if (para && para->GetRange().GetEnd() == pos)
7024 pos1 ++;
e2d0875a
JS
7025
7026 // Now see if we need to number the paragraph.
6c0ea513 7027 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
7028 {
7029 wxRichTextAttr numberingAttr;
7030 if (FindNextParagraphNumber(para, numberingAttr))
7031 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7032 }
c025e094
JS
7033 }
7034
5d7836c4
JS
7035 action->SetPosition(pos);
7036
c025e094 7037 // Use the default character style
99404ab0 7038 // Use the default character style
603f702b 7039 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
7040 {
7041 // Check whether the default style merely reflects the paragraph/basic style,
7042 // in which case don't apply it.
603f702b 7043 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
24777478 7044 wxRichTextAttr toApply;
c025e094
JS
7045 if (para)
7046 {
603f702b 7047 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 7048 wxRichTextAttr newAttr;
c025e094
JS
7049 // This filters out attributes that are accounted for by the current
7050 // paragraph/basic style
7051 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7052 }
7053 else
7054 toApply = defaultStyle;
7055
7056 if (!toApply.IsDefault())
7057 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7058 }
99404ab0 7059
5d7836c4 7060 // Set the range we'll need to delete in Undo
c025e094 7061 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 7062
603f702b 7063 buffer->SubmitAction(action);
7fe8059f 7064
5d7836c4
JS
7065 return true;
7066}
7067
7068/// Submit command to insert the given image
24777478
JS
7069bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7070 const wxRichTextAttr& textAttr)
5d7836c4 7071{
4e63bfb9 7072 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
7073}
7074
7075/// Submit command to insert the given image
4e63bfb9
JS
7076bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7077 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
7078 const wxRichTextAttr& textAttr)
7079{
7080 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7081
24777478
JS
7082 wxRichTextAttr* p = NULL;
7083 wxRichTextAttr paraAttr;
fe5aa22c
JS
7084 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7085 {
603f702b 7086 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
7087 if (!paraAttr.IsDefault())
7088 p = & paraAttr;
7089 }
7090
603f702b 7091 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 7092
5d7836c4 7093 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
7094 if (p)
7095 newPara->SetAttributes(*p);
7096
5d7836c4
JS
7097 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7098 newPara->AppendChild(imageObject);
24777478 7099 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7100 action->GetNewParagraphs().AppendChild(newPara);
7101 action->GetNewParagraphs().UpdateRanges();
7102
7103 action->GetNewParagraphs().SetPartialParagraph(true);
7104
7105 action->SetPosition(pos);
7106
7107 // Set the range we'll need to delete in Undo
7108 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7109
603f702b 7110 buffer->SubmitAction(action);
7fe8059f 7111
5d7836c4
JS
7112 return true;
7113}
7114
cdaed652 7115// Insert an object with no change of it
603f702b
JS
7116wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7117{
4e63bfb9 7118 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7119}
7120
7121// Insert an object with no change of it
4e63bfb9 7122wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7123{
603f702b 7124 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7125
24777478
JS
7126 wxRichTextAttr* p = NULL;
7127 wxRichTextAttr paraAttr;
cdaed652
VZ
7128 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7129 {
603f702b 7130 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7131 if (!paraAttr.IsDefault())
7132 p = & paraAttr;
7133 }
7134
603f702b 7135 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652
VZ
7136
7137 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7138 if (p)
7139 newPara->SetAttributes(*p);
7140
7141 newPara->AppendChild(object);
7142 action->GetNewParagraphs().AppendChild(newPara);
7143 action->GetNewParagraphs().UpdateRanges();
7144
7145 action->GetNewParagraphs().SetPartialParagraph(true);
7146
7147 action->SetPosition(pos);
7148
7149 // Set the range we'll need to delete in Undo
7150 action->SetRange(wxRichTextRange(pos, pos));
7151
603f702b 7152 buffer->SubmitAction(action);
cdaed652 7153
603f702b
JS
7154 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7155 return obj;
cdaed652 7156}
603f702b 7157
7c9fdebe
JS
7158wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7159 const wxRichTextProperties& properties,
7160 wxRichTextCtrl* ctrl, int flags,
7161 const wxRichTextAttr& textAttr)
7162{
7163 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7164
7165 wxRichTextAttr* p = NULL;
7166 wxRichTextAttr paraAttr;
7167 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7168 {
7169 paraAttr = GetStyleForNewParagraph(buffer, pos);
7170 if (!paraAttr.IsDefault())
7171 p = & paraAttr;
7172 }
7173
7174 wxRichTextAttr attr(buffer->GetDefaultStyle());
7175
7176 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7177 if (p)
7178 newPara->SetAttributes(*p);
7179
7180 wxRichTextField* fieldObject = new wxRichTextField();
7181 fieldObject->wxRichTextObject::SetProperties(properties);
7182 fieldObject->SetFieldType(fieldType);
7183 fieldObject->SetAttributes(textAttr);
7184 newPara->AppendChild(fieldObject);
7185 action->GetNewParagraphs().AppendChild(newPara);
7186 action->GetNewParagraphs().UpdateRanges();
7187 action->GetNewParagraphs().SetPartialParagraph(true);
7188 action->SetPosition(pos);
7189
7190 // Set the range we'll need to delete in Undo
7191 action->SetRange(wxRichTextRange(pos, pos));
7192
7193 buffer->SubmitAction(action);
7194
7195 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7196 return obj;
7197}
7198
fe5aa22c
JS
7199/// Get the style that is appropriate for a new paragraph at this position.
7200/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7201/// style.
603f702b 7202wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7203{
7204 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7205 if (para)
7206 {
24777478 7207 wxRichTextAttr attr;
d2d0adc7 7208 bool foundAttributes = false;
3e541562 7209
d2d0adc7 7210 // Look for a matching paragraph style
603f702b 7211 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7212 {
603f702b 7213 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7214 if (paraDef)
fe5aa22c 7215 {
caad0109
JS
7216 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7217 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7218 {
603f702b 7219 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7220 if (nextParaDef)
7221 {
7222 foundAttributes = true;
603f702b 7223 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7224 }
7225 }
3e541562 7226
d2d0adc7
JS
7227 // If we didn't find the 'next style', use this style instead.
7228 if (!foundAttributes)
7229 {
7230 foundAttributes = true;
603f702b 7231 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7232 }
fe5aa22c
JS
7233 }
7234 }
e2d0875a
JS
7235
7236 // Also apply list style if present
603f702b 7237 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7238 {
603f702b 7239 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7240 if (listDef)
7241 {
7242 int thisIndent = para->GetAttributes().GetLeftIndent();
7243 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7244
7245 // Apply the overall list style, and item style for this level
603f702b 7246 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7247 wxRichTextApplyStyle(attr, listStyle);
7248 attr.SetOutlineLevel(thisLevel);
7249 if (para->GetAttributes().HasBulletNumber())
7250 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7251 }
34b4899d 7252 }
e2d0875a 7253
d2d0adc7
JS
7254 if (!foundAttributes)
7255 {
7256 attr = para->GetAttributes();
7257 int flags = attr.GetFlags();
fe5aa22c 7258
d2d0adc7
JS
7259 // Eliminate character styles
7260 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7261 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7262 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7263 attr.SetFlags(flags);
7264 }
3e541562 7265
fe5aa22c
JS
7266 return attr;
7267 }
7268 else
24777478 7269 return wxRichTextAttr();
fe5aa22c
JS
7270}
7271
5d7836c4 7272/// Submit command to delete this range
12cc29c5 7273bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7274{
603f702b
JS
7275 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7276}
7277
7278/// Submit command to delete this range
7279bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7280{
7281 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7282
12cc29c5 7283 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7284
7285 // Set the range to delete
7286 action->SetRange(range);
7fe8059f 7287
5d7836c4
JS
7288 // Copy the fragment that we'll need to restore in Undo
7289 CopyFragment(range, action->GetOldParagraphs());
7290
6c0ea513
JS
7291 // See if we're deleting a paragraph marker, in which case we need to
7292 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7293 if (range.GetStart() == range.GetEnd())
5d7836c4 7294 {
6c0ea513
JS
7295 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7296 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7297 {
6c0ea513
JS
7298 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7299 if (nextPara && nextPara != para)
5d7836c4 7300 {
6c0ea513
JS
7301 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7302 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7303 }
7304 }
7305 }
7306
603f702b 7307 buffer->SubmitAction(action);
7fe8059f 7308
5d7836c4
JS
7309 return true;
7310}
7311
7312/// Collapse undo/redo commands
7313bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7314{
7315 if (m_batchedCommandDepth == 0)
7316 {
7317 wxASSERT(m_batchedCommand == NULL);
7318 if (m_batchedCommand)
7319 {
0745f364 7320 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7321 }
7322 m_batchedCommand = new wxRichTextCommand(cmdName);
7323 }
7324
7fe8059f 7325 m_batchedCommandDepth ++;
5d7836c4
JS
7326
7327 return true;
7328}
7329
7330/// Collapse undo/redo commands
7331bool wxRichTextBuffer::EndBatchUndo()
7332{
7333 m_batchedCommandDepth --;
7334
7335 wxASSERT(m_batchedCommandDepth >= 0);
7336 wxASSERT(m_batchedCommand != NULL);
7337
7338 if (m_batchedCommandDepth == 0)
7339 {
0745f364 7340 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7341 m_batchedCommand = NULL;
7342 }
7343
7344 return true;
7345}
7346
7347/// Submit immediately, or delay according to whether collapsing is on
7348bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7349{
cc2aecde
JS
7350 if (action && !action->GetNewParagraphs().IsEmpty())
7351 PrepareContent(action->GetNewParagraphs());
7352
5d7836c4 7353 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7354 {
7355 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7356 cmd->AddAction(action);
7357 cmd->Do();
7358 cmd->GetActions().Clear();
7359 delete cmd;
7360
5d7836c4 7361 m_batchedCommand->AddAction(action);
0745f364 7362 }
5d7836c4
JS
7363 else
7364 {
7365 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7366 cmd->AddAction(action);
7367
7368 // Only store it if we're not suppressing undo.
7369 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7370 }
7371
7372 return true;
7373}
7374
7375/// Begin suppressing undo/redo commands.
7376bool wxRichTextBuffer::BeginSuppressUndo()
7377{
7fe8059f 7378 m_suppressUndo ++;
5d7836c4
JS
7379
7380 return true;
7381}
7382
7383/// End suppressing undo/redo commands.
7384bool wxRichTextBuffer::EndSuppressUndo()
7385{
7fe8059f 7386 m_suppressUndo --;
5d7836c4
JS
7387
7388 return true;
7389}
7390
7391/// Begin using a style
24777478 7392bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7393{
24777478 7394 wxRichTextAttr newStyle(GetDefaultStyle());
5d7836c4
JS
7395
7396 // Save the old default style
24777478 7397 m_attributeStack.Append((wxObject*) new wxRichTextAttr(GetDefaultStyle()));
5d7836c4
JS
7398
7399 wxRichTextApplyStyle(newStyle, style);
7400 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7401
7402 SetDefaultStyle(newStyle);
7403
5d7836c4
JS
7404 return true;
7405}
7406
7407/// End the style
7408bool wxRichTextBuffer::EndStyle()
7409{
63886f6d 7410 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7411 {
7412 wxLogDebug(_("Too many EndStyle calls!"));
7413 return false;
7414 }
7415
09f14108 7416 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7417 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7418 m_attributeStack.Erase(node);
5d7836c4
JS
7419
7420 SetDefaultStyle(*attr);
7421
7422 delete attr;
7423 return true;
7424}
7425
7426/// End all styles
7427bool wxRichTextBuffer::EndAllStyles()
7428{
7429 while (m_attributeStack.GetCount() != 0)
7430 EndStyle();
7431 return true;
7432}
7433
7434/// Clear the style stack
7435void wxRichTextBuffer::ClearStyleStack()
7436{
09f14108 7437 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7438 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7439 m_attributeStack.Clear();
7440}
7441
7442/// Begin using bold
7443bool wxRichTextBuffer::BeginBold()
7444{
24777478 7445 wxRichTextAttr attr;
7d76fbd5 7446 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7447
5d7836c4
JS
7448 return BeginStyle(attr);
7449}
7450
7451/// Begin using italic
7452bool wxRichTextBuffer::BeginItalic()
7453{
24777478 7454 wxRichTextAttr attr;
7d76fbd5 7455 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7456
5d7836c4
JS
7457 return BeginStyle(attr);
7458}
7459
7460/// Begin using underline
7461bool wxRichTextBuffer::BeginUnderline()
7462{
24777478 7463 wxRichTextAttr attr;
44cc96a8 7464 attr.SetFontUnderlined(true);
7fe8059f 7465
5d7836c4
JS
7466 return BeginStyle(attr);
7467}
7468
7469/// Begin using point size
7470bool wxRichTextBuffer::BeginFontSize(int pointSize)
7471{
24777478 7472 wxRichTextAttr attr;
44cc96a8 7473 attr.SetFontSize(pointSize);
7fe8059f 7474
5d7836c4
JS
7475 return BeginStyle(attr);
7476}
7477
7478/// Begin using this font
7479bool wxRichTextBuffer::BeginFont(const wxFont& font)
7480{
24777478 7481 wxRichTextAttr attr;
5d7836c4 7482 attr.SetFont(font);
7fe8059f 7483
5d7836c4
JS
7484 return BeginStyle(attr);
7485}
7486
7487/// Begin using this colour
7488bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7489{
24777478 7490 wxRichTextAttr attr;
5d7836c4
JS
7491 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7492 attr.SetTextColour(colour);
7fe8059f 7493
5d7836c4
JS
7494 return BeginStyle(attr);
7495}
7496
7497/// Begin using alignment
7498bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7499{
24777478 7500 wxRichTextAttr attr;
5d7836c4
JS
7501 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7502 attr.SetAlignment(alignment);
7fe8059f 7503
5d7836c4
JS
7504 return BeginStyle(attr);
7505}
7506
7507/// Begin left indent
7508bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7509{
24777478 7510 wxRichTextAttr attr;
5d7836c4
JS
7511 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7512 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7513
5d7836c4
JS
7514 return BeginStyle(attr);
7515}
7516
7517/// Begin right indent
7518bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7519{
24777478 7520 wxRichTextAttr attr;
5d7836c4
JS
7521 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7522 attr.SetRightIndent(rightIndent);
7fe8059f 7523
5d7836c4
JS
7524 return BeginStyle(attr);
7525}
7526
7527/// Begin paragraph spacing
7528bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7529{
7530 long flags = 0;
7531 if (before != 0)
7532 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7533 if (after != 0)
7534 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7535
24777478 7536 wxRichTextAttr attr;
5d7836c4
JS
7537 attr.SetFlags(flags);
7538 attr.SetParagraphSpacingBefore(before);
7539 attr.SetParagraphSpacingAfter(after);
7fe8059f 7540
5d7836c4
JS
7541 return BeginStyle(attr);
7542}
7543
7544/// Begin line spacing
7545bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
7546{
24777478 7547 wxRichTextAttr attr;
5d7836c4
JS
7548 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
7549 attr.SetLineSpacing(lineSpacing);
7fe8059f 7550
5d7836c4
JS
7551 return BeginStyle(attr);
7552}
7553
7554/// Begin numbered bullet
7555bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
7556{
24777478 7557 wxRichTextAttr attr;
f089713f 7558 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7559 attr.SetBulletStyle(bulletStyle);
7560 attr.SetBulletNumber(bulletNumber);
7561 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7562
5d7836c4
JS
7563 return BeginStyle(attr);
7564}
7565
7566/// Begin symbol bullet
d2d0adc7 7567bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 7568{
24777478 7569 wxRichTextAttr attr;
f089713f 7570 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7571 attr.SetBulletStyle(bulletStyle);
7572 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 7573 attr.SetBulletText(symbol);
7fe8059f 7574
5d7836c4
JS
7575 return BeginStyle(attr);
7576}
7577
f089713f
JS
7578/// Begin standard bullet
7579bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
7580{
24777478 7581 wxRichTextAttr attr;
f089713f
JS
7582 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7583 attr.SetBulletStyle(bulletStyle);
7584 attr.SetLeftIndent(leftIndent, leftSubIndent);
7585 attr.SetBulletName(bulletName);
7586
7587 return BeginStyle(attr);
7588}
7589
5d7836c4
JS
7590/// Begin named character style
7591bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
7592{
7593 if (GetStyleSheet())
7594 {
7595 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7596 if (def)
7597 {
24777478 7598 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7599 return BeginStyle(attr);
7600 }
7601 }
7602 return false;
7603}
7604
7605/// Begin named paragraph style
7606bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
7607{
7608 if (GetStyleSheet())
7609 {
7610 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
7611 if (def)
7612 {
24777478 7613 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7614 return BeginStyle(attr);
7615 }
7616 }
7617 return false;
7618}
7619
f089713f
JS
7620/// Begin named list style
7621bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
7622{
7623 if (GetStyleSheet())
7624 {
7625 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
7626 if (def)
7627 {
24777478 7628 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
7629
7630 attr.SetBulletNumber(number);
7631
7632 return BeginStyle(attr);
7633 }
7634 }
7635 return false;
7636}
7637
d2d0adc7
JS
7638/// Begin URL
7639bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
7640{
24777478 7641 wxRichTextAttr attr;
d2d0adc7
JS
7642
7643 if (!characterStyle.IsEmpty() && GetStyleSheet())
7644 {
7645 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7646 if (def)
7647 {
336d8ae9 7648 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
7649 }
7650 }
7651 attr.SetURL(url);
7652
7653 return BeginStyle(attr);
7654}
7655
5d7836c4
JS
7656/// Adds a handler to the end
7657void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
7658{
7659 sm_handlers.Append(handler);
7660}
7661
7662/// Inserts a handler at the front
7663void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
7664{
7665 sm_handlers.Insert( handler );
7666}
7667
7668/// Removes a handler
7669bool wxRichTextBuffer::RemoveHandler(const wxString& name)
7670{
7671 wxRichTextFileHandler *handler = FindHandler(name);
7672 if (handler)
7673 {
7674 sm_handlers.DeleteObject(handler);
7675 delete handler;
7676 return true;
7677 }
7678 else
7679 return false;
7680}
7681
7682/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
7683wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
7684 wxRichTextFileType imageType)
5d7836c4
JS
7685{
7686 if (imageType != wxRICHTEXT_TYPE_ANY)
7687 return FindHandler(imageType);
0ca07313 7688 else if (!filename.IsEmpty())
5d7836c4
JS
7689 {
7690 wxString path, file, ext;
a51e601e 7691 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
7692 return FindHandler(ext, imageType);
7693 }
0ca07313
JS
7694 else
7695 return NULL;
5d7836c4
JS
7696}
7697
7698
7699/// Finds a handler by name
7700wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
7701{
7702 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7703 while (node)
7704 {
7705 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7706 if (handler->GetName().Lower() == name.Lower()) return handler;
7707
7708 node = node->GetNext();
7709 }
7710 return NULL;
7711}
7712
7713/// Finds a handler by extension and type
d75a69e8 7714wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
7715{
7716 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7717 while (node)
7718 {
7719 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7720 if ( handler->GetExtension().Lower() == extension.Lower() &&
7721 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
7722 return handler;
7723 node = node->GetNext();
7724 }
7725 return 0;
7726}
7727
7728/// Finds a handler by type
d75a69e8 7729wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
7730{
7731 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7732 while (node)
7733 {
7734 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
7735 if (handler->GetType() == type) return handler;
7736 node = node->GetNext();
7737 }
7738 return NULL;
7739}
7740
7741void wxRichTextBuffer::InitStandardHandlers()
7742{
7743 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
7744 AddHandler(new wxRichTextPlainTextHandler);
7745}
7746
7747void wxRichTextBuffer::CleanUpHandlers()
7748{
7749 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7750 while (node)
7751 {
7752 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
7753 wxList::compatibility_iterator next = node->GetNext();
7754 delete handler;
7755 node = next;
7756 }
7757
7758 sm_handlers.Clear();
7759}
7760
1e967276 7761wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 7762{
1e967276
JS
7763 if (types)
7764 types->Clear();
7765
5d7836c4
JS
7766 wxString wildcard;
7767
7768 wxList::compatibility_iterator node = GetHandlers().GetFirst();
7769 int count = 0;
7770 while (node)
7771 {
7772 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 7773 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
7774 {
7775 if (combine)
7776 {
7777 if (count > 0)
7778 wildcard += wxT(";");
7779 wildcard += wxT("*.") + handler->GetExtension();
7780 }
7781 else
7782 {
7783 if (count > 0)
7784 wildcard += wxT("|");
7785 wildcard += handler->GetName();
7786 wildcard += wxT(" ");
7787 wildcard += _("files");
7788 wildcard += wxT(" (*.");
7789 wildcard += handler->GetExtension();
7790 wildcard += wxT(")|*.");
7791 wildcard += handler->GetExtension();
1e967276
JS
7792 if (types)
7793 types->Add(handler->GetType());
5d7836c4
JS
7794 }
7795 count ++;
7796 }
7797
7798 node = node->GetNext();
7799 }
7800
7801 if (combine)
7802 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
7803 return wildcard;
7804}
7805
7806/// Load a file
d75a69e8 7807bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7808{
7809 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7810 if (handler)
1e967276 7811 {
24777478 7812 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7813 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7814 bool success = handler->LoadFile(this, filename);
7815 Invalidate(wxRICHTEXT_ALL);
7816 return success;
7817 }
5d7836c4
JS
7818 else
7819 return false;
7820}
7821
7822/// Save a file
d75a69e8 7823bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7824{
7825 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7826 if (handler)
d2d0adc7
JS
7827 {
7828 handler->SetFlags(GetHandlerFlags());
5d7836c4 7829 return handler->SaveFile(this, filename);
d2d0adc7 7830 }
5d7836c4
JS
7831 else
7832 return false;
7833}
7834
7835/// Load from a stream
d75a69e8 7836bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7837{
7838 wxRichTextFileHandler* handler = FindHandler(type);
7839 if (handler)
1e967276 7840 {
24777478 7841 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7842 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7843 bool success = handler->LoadFile(this, stream);
7844 Invalidate(wxRICHTEXT_ALL);
7845 return success;
7846 }
5d7836c4
JS
7847 else
7848 return false;
7849}
7850
7851/// Save to a stream
d75a69e8 7852bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7853{
7854 wxRichTextFileHandler* handler = FindHandler(type);
7855 if (handler)
d2d0adc7
JS
7856 {
7857 handler->SetFlags(GetHandlerFlags());
5d7836c4 7858 return handler->SaveFile(this, stream);
d2d0adc7 7859 }
5d7836c4
JS
7860 else
7861 return false;
7862}
7863
7864/// Copy the range to the clipboard
7865bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
7866{
7867 bool success = false;
603f702b
JS
7868 wxRichTextParagraphLayoutBox* container = this;
7869 if (GetRichTextCtrl())
7870 container = GetRichTextCtrl()->GetFocusObject();
7871
11ef729d 7872#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 7873
d2142335 7874 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 7875 {
0ca07313
JS
7876 wxTheClipboard->Clear();
7877
7878 // Add composite object
7879
7880 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
7881
7882 {
603f702b 7883 wxString text = container->GetTextForRange(range);
0ca07313
JS
7884
7885#ifdef __WXMSW__
7886 text = wxTextFile::Translate(text, wxTextFileType_Dos);
7887#endif
7888
7889 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
7890 }
7891
7892 // Add rich text buffer data object. This needs the XML handler to be present.
7893
7894 if (FindHandler(wxRICHTEXT_TYPE_XML))
7895 {
7896 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 7897 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
7898
7899 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
7900 }
7901
7902 if (wxTheClipboard->SetData(compositeObject))
7903 success = true;
7904
5d7836c4
JS
7905 wxTheClipboard->Close();
7906 }
0ca07313 7907
39a1c2f2
WS
7908#else
7909 wxUnusedVar(range);
7910#endif
5d7836c4
JS
7911 return success;
7912}
7913
7914/// Paste the clipboard content to the buffer
7915bool wxRichTextBuffer::PasteFromClipboard(long position)
7916{
7917 bool success = false;
603f702b
JS
7918 wxRichTextParagraphLayoutBox* container = this;
7919 if (GetRichTextCtrl())
7920 container = GetRichTextCtrl()->GetFocusObject();
7921
11ef729d 7922#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
7923 if (CanPasteFromClipboard())
7924 {
7925 if (wxTheClipboard->Open())
7926 {
0ca07313
JS
7927 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7928 {
7929 wxRichTextBufferDataObject data;
7930 wxTheClipboard->GetData(data);
7931 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
7932 if (richTextBuffer)
7933 {
4e63bfb9 7934 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 7935 if (GetRichTextCtrl())
603f702b 7936 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
7937 delete richTextBuffer;
7938 }
7939 }
f7d83f24 7940 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
7941 #if wxUSE_UNICODE
7942 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
7943 #endif
f7d83f24 7944 )
5d7836c4
JS
7945 {
7946 wxTextDataObject data;
7947 wxTheClipboard->GetData(data);
7948 wxString text(data.GetText());
c21f3211
JS
7949#ifdef __WXMSW__
7950 wxString text2;
7951 text2.Alloc(text.Length()+1);
7952 size_t i;
7953 for (i = 0; i < text.Length(); i++)
7954 {
7955 wxChar ch = text[i];
7956 if (ch != wxT('\r'))
7957 text2 += ch;
7958 }
7959#else
7960 wxString text2 = text;
7961#endif
4e63bfb9 7962 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 7963
62381daa
JS
7964 if (GetRichTextCtrl())
7965 GetRichTextCtrl()->ShowPosition(position + text2.Length());
7966
5d7836c4
JS
7967 success = true;
7968 }
7969 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
7970 {
7971 wxBitmapDataObject data;
7972 wxTheClipboard->GetData(data);
7973 wxBitmap bitmap(data.GetBitmap());
7974 wxImage image(bitmap.ConvertToImage());
7975
603f702b 7976 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 7977
5d7836c4
JS
7978 action->GetNewParagraphs().AddImage(image);
7979
7980 if (action->GetNewParagraphs().GetChildCount() == 1)
7981 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 7982
9c8e10ad 7983 action->SetPosition(position+1);
7fe8059f 7984
5d7836c4 7985 // Set the range we'll need to delete in Undo
9c8e10ad 7986 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 7987
5d7836c4
JS
7988 SubmitAction(action);
7989
7990 success = true;
7991 }
7992 wxTheClipboard->Close();
7993 }
7994 }
39a1c2f2
WS
7995#else
7996 wxUnusedVar(position);
7997#endif
5d7836c4
JS
7998 return success;
7999}
8000
8001/// Can we paste from the clipboard?
8002bool wxRichTextBuffer::CanPasteFromClipboard() const
8003{
7fe8059f 8004 bool canPaste = false;
11ef729d 8005#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 8006 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 8007 {
f7d83f24
VZ
8008 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8009#if wxUSE_UNICODE
603f702b
JS
8010 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8011#endif
8012 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8013 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 8014 {
7fe8059f 8015 canPaste = true;
5d7836c4
JS
8016 }
8017 wxTheClipboard->Close();
8018 }
39a1c2f2 8019#endif
5d7836c4
JS
8020 return canPaste;
8021}
8022
8023/// Dumps contents of buffer for debugging purposes
8024void wxRichTextBuffer::Dump()
8025{
8026 wxString text;
8027 {
8028 wxStringOutputStream stream(& text);
8029 wxTextOutputStream textStream(stream);
8030 Dump(textStream);
8031 }
8032
8033 wxLogDebug(text);
8034}
8035
d2d0adc7
JS
8036/// Add an event handler
8037bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8038{
8039 m_eventHandlers.Append(handler);
8040 return true;
8041}
8042
8043/// Remove an event handler
8044bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8045{
8046 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8047 if (node)
8048 {
8049 m_eventHandlers.Erase(node);
8050 if (deleteHandler)
8051 delete handler;
3e541562 8052
d2d0adc7
JS
8053 return true;
8054 }
8055 else
8056 return false;
8057}
8058
8059/// Clear event handlers
8060void wxRichTextBuffer::ClearEventHandlers()
8061{
8062 m_eventHandlers.Clear();
8063}
8064
8065/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8066/// otherwise will stop at the first successful one.
8067bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8068{
8069 bool success = false;
8070 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8071 {
8072 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8073 if (handler->ProcessEvent(event))
8074 {
8075 success = true;
8076 if (!sendToAll)
8077 return true;
8078 }
8079 }
8080 return success;
8081}
8082
8083/// Set style sheet and notify of the change
8084bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8085{
8086 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 8087
616c7cbd 8088 wxWindowID winid = wxID_ANY;
d2d0adc7 8089 if (GetRichTextCtrl())
616c7cbd 8090 winid = GetRichTextCtrl()->GetId();
3e541562 8091
616c7cbd 8092 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 8093 event.SetEventObject(GetRichTextCtrl());
603f702b 8094 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
8095 event.SetOldStyleSheet(oldSheet);
8096 event.SetNewStyleSheet(sheet);
8097 event.Allow();
3e541562 8098
d2d0adc7
JS
8099 if (SendEvent(event) && !event.IsAllowed())
8100 {
8101 if (sheet != oldSheet)
8102 delete sheet;
8103
8104 return false;
8105 }
8106
8107 if (oldSheet && oldSheet != sheet)
8108 delete oldSheet;
8109
8110 SetStyleSheet(sheet);
8111
8112 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8113 event.SetOldStyleSheet(NULL);
8114 event.Allow();
8115
8116 return SendEvent(event);
8117}
8118
8119/// Set renderer, deleting old one
8120void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8121{
8122 if (sm_renderer)
8123 delete sm_renderer;
8124 sm_renderer = renderer;
8125}
8126
603f702b
JS
8127/// Hit-testing: returns a flag indicating hit test details, plus
8128/// information about position
8db2e3ef 8129int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 8130{
8db2e3ef 8131 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
8132 if (ret != wxRICHTEXT_HITTEST_NONE)
8133 {
8134 return ret;
8135 }
8136 else
8137 {
8138 textPosition = m_ownRange.GetEnd()-1;
8139 *obj = this;
8140 *contextObj = this;
8141 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8142 }
8143}
8144
24777478 8145bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8146{
a1b806b9 8147 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8148 {
ecb5fbf1
JS
8149 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8150 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8151 }
8152 else
8153 {
ecb5fbf1
JS
8154 wxCheckSetPen(dc, *wxBLACK_PEN);
8155 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8156 }
8157
8158 wxFont font;
44cc96a8
JS
8159 if (bulletAttr.HasFont())
8160 {
8161 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8162 }
d2d0adc7
JS
8163 else
8164 font = (*wxNORMAL_FONT);
8165
ecb5fbf1 8166 wxCheckSetFont(dc, font);
d2d0adc7
JS
8167
8168 int charHeight = dc.GetCharHeight();
3e541562 8169
d2d0adc7
JS
8170 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8171 int bulletHeight = bulletWidth;
8172
8173 int x = rect.x;
3e541562 8174
d2d0adc7
JS
8175 // Calculate the top position of the character (as opposed to the whole line height)
8176 int y = rect.y + (rect.height - charHeight);
3e541562 8177
d2d0adc7
JS
8178 // Calculate where the bullet should be positioned
8179 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8180
d2d0adc7 8181 // The margin between a bullet and text.
44219ff0 8182 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8183
d2d0adc7
JS
8184 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8185 x = rect.x + rect.width - bulletWidth - margin;
8186 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8187 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8188
d2d0adc7
JS
8189 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8190 {
8191 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8192 }
8193 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8194 {
8195 wxPoint pts[5];
8196 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8197 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8198 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8199 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8200
d2d0adc7
JS
8201 dc.DrawPolygon(4, pts);
8202 }
8203 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8204 {
8205 wxPoint pts[3];
8206 pts[0].x = x; pts[0].y = y;
8207 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8208 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8209
d2d0adc7
JS
8210 dc.DrawPolygon(3, pts);
8211 }
603f702b
JS
8212 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8213 {
8214 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8215 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8216 }
d2d0adc7
JS
8217 else // "standard/circle", and catch-all
8218 {
8219 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8220 }
8221
d2d0adc7
JS
8222 return true;
8223}
8224
24777478 8225bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8226{
8227 if (!text.empty())
8228 {
8229 wxFont font;
44cc96a8
JS
8230 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8231 {
24777478 8232 wxRichTextAttr fontAttr;
44cc96a8
JS
8233 fontAttr.SetFontSize(attr.GetFontSize());
8234 fontAttr.SetFontStyle(attr.GetFontStyle());
8235 fontAttr.SetFontWeight(attr.GetFontWeight());
8236 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8237 fontAttr.SetFontFaceName(attr.GetBulletFont());
8238 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8239 }
8240 else if (attr.HasFont())
8241 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8242 else
8243 font = (*wxNORMAL_FONT);
8244
ecb5fbf1 8245 wxCheckSetFont(dc, font);
d2d0adc7 8246
a1b806b9 8247 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8248 dc.SetTextForeground(attr.GetTextColour());
8249
04ee05f9 8250 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8251
8252 int charHeight = dc.GetCharHeight();
8253 wxCoord tw, th;
8254 dc.GetTextExtent(text, & tw, & th);
8255
8256 int x = rect.x;
8257
8258 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8259 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8260
8261 // The margin between a bullet and text.
44219ff0 8262 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8263
d2d0adc7
JS
8264 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8265 x = (rect.x + rect.width) - tw - margin;
8266 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8267 x = x + (rect.width)/2 - tw/2;
8268
8269 dc.DrawText(text, x, y);
3e541562 8270
d2d0adc7
JS
8271 return true;
8272 }
8273 else
8274 return false;
8275}
8276
24777478 8277bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8278{
8279 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8280 // with the buffer. The store will allow retrieval from memory, disk or other means.
8281 return false;
8282}
8283
8284/// Enumerate the standard bullet names currently supported
8285bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8286{
04529b2a 8287 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8288 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8289 bulletNames.Add(wxTRANSLATE("standard/square"));
8290 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8291 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8292
8293 return true;
8294}
5d7836c4 8295
bec80f4f
JS
8296/*!
8297 * wxRichTextBox
8298 */
8299
603f702b 8300IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8301
8302wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8303 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8304{
8305}
8306
8307/// Draw the item
8db2e3ef 8308bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8309{
603f702b
JS
8310 if (!IsShown())
8311 return true;
5ad9ae3a 8312
603f702b
JS
8313 // TODO: if the active object in the control, draw an indication.
8314 // We need to add the concept of active object, and not just focus object,
8315 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8316 // Ultimately we would like to be able to interactively resize an active object
8317 // using drag handles.
8db2e3ef 8318 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8319}
5ad9ae3a 8320
603f702b
JS
8321/// Copy
8322void wxRichTextBox::Copy(const wxRichTextBox& obj)
8323{
8324 wxRichTextParagraphLayoutBox::Copy(obj);
8325}
8326
8327// Edit properties via a GUI
8328bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8329{
8330 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8331 boxDlg.SetAttributes(GetAttributes());
8332
8333 if (boxDlg.ShowModal() == wxID_OK)
8334 {
8335 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8336 // indeterminate in the object.
8337 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8338 return true;
5ad9ae3a 8339 }
603f702b
JS
8340 else
8341 return false;
bec80f4f
JS
8342}
8343
7c9fdebe
JS
8344/*!
8345 * wxRichTextField
8346 */
8347
8348IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8349
8350wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8351 wxRichTextParagraphLayoutBox(parent)
8352{
8353 SetFieldType(fieldType);
8354}
8355
8356/// Draw the item
8357bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8358{
8359 if (!IsShown())
8360 return true;
8361
8362 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8363 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8364 return true;
8365
8366 // Fallback; but don't draw guidelines.
8367 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8368 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8369}
8370
8371bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8372{
8373 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8374 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8375 return true;
8376
8377 // Fallback
8378 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8379}
8380
8381bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8382{
8383 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8384 if (fieldType)
8385 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents);
8386
8387 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
8388}
8389
8390/// Calculate range
8391void wxRichTextField::CalculateRange(long start, long& end)
8392{
8393 if (IsTopLevel())
8394 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8395 else
8396 wxRichTextObject::CalculateRange(start, end);
8397}
8398
8399/// Copy
8400void wxRichTextField::Copy(const wxRichTextField& obj)
8401{
8402 wxRichTextParagraphLayoutBox::Copy(obj);
8403
8404 UpdateField();
8405}
8406
8407// Edit properties via a GUI
8408bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8409{
8410 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8411 if (fieldType)
8412 return fieldType->EditProperties(this, parent, buffer);
8413
8414 return false;
8415}
8416
8417bool wxRichTextField::CanEditProperties() const
8418{
8419 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8420 if (fieldType)
8421 return fieldType->CanEditProperties((wxRichTextField*) this);
8422
8423 return false;
8424}
8425
8426wxString wxRichTextField::GetPropertiesMenuLabel() const
8427{
8428 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8429 if (fieldType)
8430 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8431
8432 return wxEmptyString;
8433}
8434
8435bool wxRichTextField::UpdateField()
8436{
8437 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8438 if (fieldType)
8439 return fieldType->UpdateField((wxRichTextField*) this);
8440
8441 return false;
8442}
8443
8444bool wxRichTextField::IsTopLevel() const
8445{
8446 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8447 if (fieldType)
8448 return fieldType->IsTopLevel((wxRichTextField*) this);
8449
8450 return true;
8451}
8452
8453IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8454
8455IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8456
8457wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8458{
8459 Init();
8460
8461 SetName(name);
8462 SetLabel(label);
8463 SetDisplayStyle(displayStyle);
8464}
8465
8466wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8467{
8468 Init();
8469
8470 SetName(name);
8471 SetBitmap(bitmap);
8472 SetDisplayStyle(displayStyle);
8473}
8474
8475void wxRichTextFieldTypeStandard::Init()
8476{
8477 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8478 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8479 m_textColour = *wxWHITE;
8480 m_borderColour = *wxBLACK;
8481 m_backgroundColour = *wxBLACK;
8482 m_verticalPadding = 1;
8483 m_horizontalPadding = 3;
8484 m_horizontalMargin = 2;
8485 m_verticalMargin = 0;
8486}
8487
8488void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
8489{
8490 wxRichTextFieldType::Copy(field);
8491
8492 m_label = field.m_label;
8493 m_displayStyle = field.m_displayStyle;
8494 m_font = field.m_font;
8495 m_textColour = field.m_textColour;
8496 m_borderColour = field.m_borderColour;
8497 m_backgroundColour = field.m_backgroundColour;
8498 m_verticalPadding = field.m_verticalPadding;
8499 m_horizontalPadding = field.m_horizontalPadding;
8500 m_horizontalMargin = field.m_horizontalMargin;
8501 m_bitmap = field.m_bitmap;
8502}
8503
8504bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
8505{
8506 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8507 return false; // USe default composite drawing
8508 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8509 {
8510 int borderSize = 1;
8511
8512 wxPen borderPen(m_borderColour, 1, wxSOLID);
8513 wxBrush backgroundBrush(m_backgroundColour);
8514 wxColour textColour(m_textColour);
8515
8516 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8517 {
8518 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
8519 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
8520
8521 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
8522 backgroundBrush = wxBrush(highlightColour);
8523
8524 wxCheckSetBrush(dc, backgroundBrush);
8525 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
8526 dc.DrawRectangle(rect);
8527 }
8528
8529 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
8530 borderSize = 0;
8531
8532 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8533 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
8534 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
8535
8536 // clientArea is where the text is actually written
8537 wxRect clientArea = objectRect;
8538
8539 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
8540 {
8541 dc.SetPen(borderPen);
8542 dc.SetBrush(backgroundBrush);
8543 dc.DrawRoundedRectangle(objectRect, 4.0);
8544 }
8545 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
8546 {
8547 int arrowLength = objectRect.height/2;
8548 clientArea.width -= (arrowLength - m_horizontalPadding);
8549
8550 wxPoint pts[5];
8551 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
8552 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
8553 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
8554 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
8555 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
8556 dc.SetPen(borderPen);
8557 dc.SetBrush(backgroundBrush);
8558 dc.DrawPolygon(5, pts);
8559 }
8560 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
8561 {
8562 int arrowLength = objectRect.height/2;
8563 clientArea.width -= (arrowLength - m_horizontalPadding);
8564 clientArea.x += (arrowLength - m_horizontalPadding);
8565
8566 wxPoint pts[5];
8567 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
8568 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
8569 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
8570 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
8571 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
8572 dc.SetPen(borderPen);
8573 dc.SetBrush(backgroundBrush);
8574 dc.DrawPolygon(5, pts);
8575 }
8576
8577 if (m_bitmap.IsOk())
8578 {
8579 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
8580 int y = clientArea.y + m_verticalPadding;
8581 dc.DrawBitmap(m_bitmap, x, y, true);
8582
8583 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8584 {
8585 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8586 wxCheckSetPen(dc, *wxBLACK_PEN);
8587 dc.SetLogicalFunction(wxINVERT);
8588 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
8589 dc.SetLogicalFunction(wxCOPY);
8590 }
8591 }
8592 else
8593 {
8594 wxString label(m_label);
8595 if (label.IsEmpty())
8596 label = wxT("??");
8597 int w, h, maxDescent;
8598 dc.SetFont(m_font);
8599 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
8600 dc.SetTextForeground(textColour);
8601
8602 int x = clientArea.x + (clientArea.width - w)/2;
8603 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
8604 dc.DrawText(m_label, x, y);
8605 }
8606 }
8607
8608 return true;
8609}
8610
8611bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
8612{
8613 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8614 return false; // USe default composite layout
8615
8616 wxSize size = GetSize(obj, dc, context, style);
8617 obj->SetCachedSize(size);
8618 obj->SetMinSize(size);
8619 obj->SetMaxSize(size);
8620 return true;
8621}
8622
8623bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8624{
8625 if (IsTopLevel(obj))
8626 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position);
8627 else
8628 {
8629 wxSize sz = GetSize(obj, dc, context, 0);
8630 if (partialExtents)
8631 {
8632 int lastSize;
8633 if (partialExtents->GetCount() > 0)
8634 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
8635 else
8636 lastSize = 0;
8637 partialExtents->Add(lastSize + sz.x);
8638 }
8639 size = sz;
8640 return true;
8641 }
8642}
8643
8644wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
8645{
8646 int borderSize = 1;
8647 int w = 0, h = 0, maxDescent = 0;
8648
8649 wxSize sz;
8650 if (m_bitmap.IsOk())
8651 {
8652 w = m_bitmap.GetWidth();
8653 h = m_bitmap.GetHeight();
8654
8655 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
8656 }
8657 else
8658 {
8659 wxString label(m_label);
8660 if (label.IsEmpty())
8661 label = wxT("??");
8662 dc.SetFont(m_font);
8663 dc.GetTextExtent(label, & w, &h, & maxDescent);
8664
8665 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
8666 }
8667
8668 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
8669 {
8670 sz.x += borderSize*2;
8671 sz.y += borderSize*2;
8672 }
8673
8674 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
8675 {
8676 // Add space for the arrow
8677 sz.x += (sz.y/2 - m_horizontalPadding);
8678 }
8679
8680 return sz;
8681}
8682
603f702b
JS
8683IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
8684
8685wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
8686 wxRichTextBox(parent)
bec80f4f 8687{
603f702b
JS
8688}
8689
8690/// Draw the item
8db2e3ef 8691bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 8692{
8db2e3ef 8693 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
8694}
8695
8696/// Copy
8697void wxRichTextCell::Copy(const wxRichTextCell& obj)
8698{
8699 wxRichTextBox::Copy(obj);
8700}
8701
8702// Edit properties via a GUI
8703bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8704{
8705 // We need to gather common attributes for all selected cells.
8706
8707 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
8708 bool multipleCells = false;
8709 wxRichTextAttr attr;
8710
8711 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
8712 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 8713 {
603f702b
JS
8714 wxRichTextAttr clashingAttr, absentAttr;
8715 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8716 size_t i;
8717 int selectedCellCount = 0;
8718 for (i = 0; i < sel.GetCount(); i++)
8719 {
8720 const wxRichTextRange& range = sel[i];
8721 wxRichTextCell* cell = table->GetCell(range.GetStart());
8722 if (cell)
8723 {
8724 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 8725
603f702b
JS
8726 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
8727
8728 selectedCellCount ++;
8729 }
8730 }
8731 multipleCells = selectedCellCount > 1;
5ad9ae3a 8732 }
603f702b
JS
8733 else
8734 {
8735 attr = GetAttributes();
8736 }
8737
8738 wxString caption;
8739 if (multipleCells)
8740 caption = _("Multiple Cell Properties");
8741 else
8742 caption = _("Cell Properties");
8743
8744 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
8745 cellDlg.SetAttributes(attr);
8746
8747 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
8748 if (sizePage)
8749 {
8750 // We don't want position and floating controls for a cell.
8751 sizePage->ShowPositionControls(false);
8752 sizePage->ShowFloatingControls(false);
8753 }
8754
8755 if (cellDlg.ShowModal() == wxID_OK)
8756 {
8757 if (multipleCells)
8758 {
8759 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8760 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8761 // since it may represent clashing attributes across multiple objects.
8762 table->SetCellStyle(sel, attr);
8763 }
8764 else
8765 // For a single object, indeterminate attributes set by the user should be reflected in the
8766 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8767 // the style directly instead of applying (which ignores indeterminate attributes,
8768 // leaving them as they were).
8769 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8770 return true;
8771 }
8772 else
8773 return false;
8774}
8775
8776WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
8777
8778IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
8779
8780wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
8781{
8782 m_rowCount = 0;
8783 m_colCount = 0;
8784}
5ad9ae3a 8785
603f702b 8786// Draws the object.
8db2e3ef 8787bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 8788{
8db2e3ef 8789 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
8790}
8791
603f702b
JS
8792WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
8793WX_DEFINE_OBJARRAY(wxRichTextRectArray);
8794
8795// Lays the object out. rect is the space available for layout. Often it will
8796// be the specified overall space for this object, if trying to constrain
8797// layout to a particular size, or it could be the total space available in the
8798// parent. rect is the overall size, so we must subtract margins and padding.
8799// to get the actual available space.
8db2e3ef 8800bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 8801{
603f702b
JS
8802 SetPosition(rect.GetPosition());
8803
8804 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8805 // minimum size if within alloted size, then divide up remaining size
8806 // between rows/cols.
8807
8808 double scale = 1.0;
8809 wxRichTextBuffer* buffer = GetBuffer();
8810 if (buffer) scale = buffer->GetScale();
8811
8db2e3ef 8812 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
8813 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
8814
8db2e3ef
JS
8815 wxRichTextAttr attr(GetAttributes());
8816 context.ApplyVirtualAttributes(attr, this);
8817
603f702b
JS
8818 // If we have no fixed table size, and assuming we're not pushed for
8819 // space, then we don't have to try to stretch the table to fit the contents.
8820 bool stretchToFitTableWidth = false;
8821
8822 int tableWidth = rect.width;
8db2e3ef 8823 if (attr.GetTextBoxAttr().GetWidth().IsValid())
603f702b 8824 {
8db2e3ef 8825 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
8826
8827 // Fixed table width, so we do want to stretch columns out if necessary.
8828 stretchToFitTableWidth = true;
8829
8830 // Shouldn't be able to exceed the size passed to this function
8831 tableWidth = wxMin(rect.width, tableWidth);
8832 }
8833
8834 // Get internal padding
36307fdf 8835 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
8836 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8837 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
8838 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8839 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
8840
8841 // Assume that left and top padding are also used for inter-cell padding.
8842 int paddingX = paddingLeft;
8843 int paddingY = paddingTop;
8844
8845 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 8846 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
8847
8848 // Internal table width - the area for content
8849 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
8850
8851 int rowCount = m_cells.GetCount();
8852 if (m_colCount == 0 || rowCount == 0)
8853 {
8854 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
8855 SetCachedSize(overallRect.GetSize());
8856
8857 // Zero content size
8858 SetMinSize(overallRect.GetSize());
8859 SetMaxSize(GetMinSize());
8860 return true;
8861 }
8862
8863 // The final calculated widths
bb7bbd12
JS
8864 wxArrayInt colWidths;
8865 colWidths.Add(0, m_colCount);
603f702b 8866
bb7bbd12
JS
8867 wxArrayInt absoluteColWidths;
8868 absoluteColWidths.Add(0, m_colCount);
7c9fdebe 8869
bb7bbd12
JS
8870 wxArrayInt percentageColWidths;
8871 percentageColWidths.Add(0, m_colCount);
603f702b
JS
8872 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8873 // These are only relevant when the first column contains spanning information.
8874 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
8875 wxArrayInt maxColWidths;
8876 maxColWidths.Add(0, m_colCount);
8877 wxArrayInt minColWidths;
8878 minColWidths.Add(0, m_colCount);
603f702b
JS
8879
8880 wxSize tableSize(tableWidth, 0);
8881
8882 int i, j, k;
8883
8884 for (i = 0; i < m_colCount; i++)
8885 {
8886 absoluteColWidths[i] = 0;
8887 // absoluteColWidthsSpanning[i] = 0;
8888 percentageColWidths[i] = -1;
8889 // percentageColWidthsSpanning[i] = -1;
8890 colWidths[i] = 0;
8891 maxColWidths[i] = 0;
8892 minColWidths[i] = 0;
8893 // columnSpans[i] = 1;
8894 }
8895
8896 // (0) Determine which cells are visible according to spans
8897 // 1 2 3 4 5
8898 // __________________
8899 // | | | | | 1
8900 // |------| |----|
8901 // |------| | | 2
8902 // |------| | | 3
8903 // |------------------|
8904 // |__________________| 4
8905
8906 // To calculate cell visibility:
8907 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8908 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8909 // that cell, hide the cell.
8910
8911 // We can also use this array to match the size of spanning cells to the grid. Or just do
8912 // this when we iterate through all cells.
8913
8914 // 0.1: add spanning cells to an array
8915 wxRichTextRectArray rectArray;
8916 for (j = 0; j < m_rowCount; j++)
8917 {
8918 for (i = 0; i < m_colCount; i++)
8919 {
8920 wxRichTextBox* cell = GetCell(j, i);
8921 int colSpan = 1, rowSpan = 1;
8922 if (cell->GetProperties().HasProperty(wxT("colspan")))
8923 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8924 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8925 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8926 if (colSpan > 1 || rowSpan > 1)
8927 {
8928 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
8929 }
8930 }
8931 }
8932 // 0.2: find which cells are subsumed by a spanning cell
8933 for (j = 0; j < m_rowCount; j++)
8934 {
8935 for (i = 0; i < m_colCount; i++)
8936 {
8937 wxRichTextBox* cell = GetCell(j, i);
8938 if (rectArray.GetCount() == 0)
8939 {
8940 cell->Show(true);
8941 }
8942 else
8943 {
8944 int colSpan = 1, rowSpan = 1;
8945 if (cell->GetProperties().HasProperty(wxT("colspan")))
8946 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8947 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8948 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8949 if (colSpan > 1 || rowSpan > 1)
8950 {
8951 // Assume all spanning cells are shown
8952 cell->Show(true);
8953 }
8954 else
8955 {
8956 bool shown = true;
8957 for (k = 0; k < (int) rectArray.GetCount(); k++)
8958 {
8959 if (rectArray[k].Contains(wxPoint(i, j)))
8960 {
8961 shown = false;
8962 break;
8963 }
8964 }
8965 cell->Show(shown);
8966 }
8967 }
8968 }
8969 }
8970
8971 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8972 // overlap with a spanned cell starting at a previous column position.
8973 // This means we need to keep an array of rects so we can check. However
8974 // it does also mean that some spans simply may not be taken into account
8975 // where there are different spans happening on different rows. In these cases,
8976 // they will simply be as wide as their constituent columns.
8977
8978 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8979 // the absolute or percentage width of each column.
8980
8981 for (j = 0; j < m_rowCount; j++)
8982 {
8983 // First get the overall margins so we can calculate percentage widths based on
8984 // the available content space for all cells on the row
8985
8986 int overallRowContentMargin = 0;
8987 int visibleCellCount = 0;
8988
8989 for (i = 0; i < m_colCount; i++)
8990 {
8991 wxRichTextBox* cell = GetCell(j, i);
8992 if (cell->IsShown())
8993 {
8994 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
8995 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
8996
8997 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
8998 visibleCellCount ++;
8999 }
9000 }
9001
9002 // Add in inter-cell padding
9003 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9004
9005 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9006 wxSize rowTableSize(rowContentWidth, 0);
9007 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9008
9009 for (i = 0; i < m_colCount; i++)
9010 {
9011 wxRichTextBox* cell = GetCell(j, i);
9012 if (cell->IsShown())
9013 {
9014 int colSpan = 1;
9015 if (cell->GetProperties().HasProperty(wxT("colspan")))
9016 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9017
9018 // Lay out cell to find min/max widths
9019 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9020 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
9021
9022 if (colSpan == 1)
9023 {
9024 int absoluteCellWidth = -1;
9025 int percentageCellWidth = -1;
9026
9027 // I think we need to calculate percentages from the internal table size,
9028 // minus the padding between cells which we'll need to calculate from the
9029 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9030 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9031 // so if we want to conform to that we'll need to add in the overall cell margins.
9032 // However, this will make it difficult to specify percentages that add up to
9033 // 100% and still fit within the table width.
9034 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9035 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9036 // If we're using internal content size for the width, we would calculate the
9037 // the overall cell width for n cells as:
9038 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9039 // + thisOverallCellMargin
9040 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9041 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9042
9043 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9044 {
9045 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9046 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9047 {
9048 percentageCellWidth = w;
9049 }
9050 else
9051 {
9052 absoluteCellWidth = w;
9053 }
9054 // Override absolute width with minimum width if necessary
9055 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9056 absoluteCellWidth = cell->GetMinSize().x;
9057 }
9058
9059 if (absoluteCellWidth != -1)
9060 {
9061 if (absoluteCellWidth > absoluteColWidths[i])
9062 absoluteColWidths[i] = absoluteCellWidth;
9063 }
9064
9065 if (percentageCellWidth != -1)
9066 {
9067 if (percentageCellWidth > percentageColWidths[i])
9068 percentageColWidths[i] = percentageCellWidth;
9069 }
9070
9071 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9072 minColWidths[i] = cell->GetMinSize().x;
9073 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9074 maxColWidths[i] = cell->GetMaxSize().x;
9075 }
9076 }
9077 }
9078 }
9079
9080 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9081 // TODO: simply merge this into (1).
9082 for (i = 0; i < m_colCount; i++)
9083 {
9084 if (absoluteColWidths[i] > 0)
9085 {
9086 colWidths[i] = absoluteColWidths[i];
9087 }
9088 else if (percentageColWidths[i] > 0)
9089 {
9090 colWidths[i] = percentageColWidths[i];
9091
9092 // This is rubbish - we calculated the absolute widths from percentages, so
9093 // we can't do it again here.
9094 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9095 }
9096 }
9097
9098 // (3) Process absolute or proportional widths of spanning columns,
9099 // now that we know what our fixed column widths are going to be.
9100 // Spanned cells will try to adjust columns so the span will fit.
9101 // Even existing fixed column widths can be expanded if necessary.
9102 // Actually, currently fixed columns widths aren't adjusted; instead,
9103 // the algorithm favours earlier rows and adjusts unspecified column widths
9104 // the first time only. After that, we can't know whether the column has been
9105 // specified explicitly or not. (We could make a note if necessary.)
9106 for (j = 0; j < m_rowCount; j++)
9107 {
9108 // First get the overall margins so we can calculate percentage widths based on
9109 // the available content space for all cells on the row
9110
9111 int overallRowContentMargin = 0;
9112 int visibleCellCount = 0;
9113
9114 for (i = 0; i < m_colCount; i++)
9115 {
9116 wxRichTextBox* cell = GetCell(j, i);
9117 if (cell->IsShown())
9118 {
9119 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9120 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9121
9122 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9123 visibleCellCount ++;
9124 }
9125 }
9126
9127 // Add in inter-cell padding
9128 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9129
9130 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9131 wxSize rowTableSize(rowContentWidth, 0);
9132 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9133
9134 for (i = 0; i < m_colCount; i++)
9135 {
9136 wxRichTextBox* cell = GetCell(j, i);
9137 if (cell->IsShown())
9138 {
9139 int colSpan = 1;
9140 if (cell->GetProperties().HasProperty(wxT("colspan")))
9141 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9142
9143 if (colSpan > 1)
9144 {
9145 int spans = wxMin(colSpan, m_colCount - i);
9146 int cellWidth = 0;
9147 if (spans > 0)
9148 {
9149 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9150 {
9151 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9152 // Override absolute width with minimum width if necessary
9153 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9154 cellWidth = cell->GetMinSize().x;
9155 }
9156 else
9157 {
9158 // Do we want to do this? It's the only chance we get to
9159 // use the cell's min/max sizes, so we need to work out
9160 // how we're going to balance the unspecified spanning cell
9161 // width with the possibility more-constrained constituent cell widths.
9162 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9163 // don't want to constraint all the spanned columns to fit into this cell.
9164 // OK, let's say that if any of the constituent columns don't fit,
9165 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9166 // cells to the columns later.
9167 cellWidth = cell->GetMinSize().x;
9168 if (cell->GetMaxSize().x > cellWidth)
9169 cellWidth = cell->GetMaxSize().x;
9170 }
9171
9172 // Subtract the padding between cells
9173 int spanningWidth = cellWidth;
9174 spanningWidth -= paddingX * (spans-1);
9175
9176 if (spanningWidth > 0)
9177 {
9178 // Now share the spanning width between columns within that span
9179 // TODO: take into account min widths of columns within the span
9180 int spanningWidthLeft = spanningWidth;
9181 int stretchColCount = 0;
9182 for (k = i; k < (i+spans); k++)
9183 {
9184 if (colWidths[k] > 0) // absolute or proportional width has been specified
9185 spanningWidthLeft -= colWidths[k];
9186 else
9187 stretchColCount ++;
9188 }
9189 // Now divide what's left between the remaining columns
9190 int colShare = 0;
9191 if (stretchColCount > 0)
9192 colShare = spanningWidthLeft / stretchColCount;
9193 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9194
9195 // If fixed-width columns are currently too big, then we'll later
9196 // stretch the spanned cell to fit.
9197
9198 if (spanningWidthLeft > 0)
9199 {
9200 for (k = i; k < (i+spans); k++)
9201 {
9202 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9203 {
9204 int newWidth = colShare;
9205 if (k == (i+spans-1))
9206 newWidth += colShareRemainder; // ensure all pixels are filled
9207 colWidths[k] = newWidth;
9208 }
9209 }
9210 }
9211 }
9212 }
9213 }
9214 }
9215 }
9216 }
9217
9218 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9219 // TODO: take into account min widths of columns within the span
9220 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9221 int widthLeft = tableWidthMinusPadding;
9222 int stretchColCount = 0;
9223 for (i = 0; i < m_colCount; i++)
9224 {
9225 // TODO: we need to take into account min widths.
9226 // Subtract min width from width left, then
9227 // add the colShare to the min width
9228 if (colWidths[i] > 0) // absolute or proportional width has been specified
9229 widthLeft -= colWidths[i];
9230 else
9231 {
9232 if (minColWidths[i] > 0)
9233 widthLeft -= minColWidths[i];
9234
9235 stretchColCount ++;
9236 }
9237 }
9238
9239 // Now divide what's left between the remaining columns
9240 int colShare = 0;
9241 if (stretchColCount > 0)
9242 colShare = widthLeft / stretchColCount;
9243 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9244
9245 // Check we don't have enough space, in which case shrink all columns, overriding
9246 // any absolute/proportional widths
9247 // TODO: actually we would like to divide up the shrinkage according to size.
9248 // How do we calculate the proportions that will achieve this?
9249 // Could first choose an arbitrary value for stretching cells, and then calculate
9250 // factors to multiply each width by.
9251 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9252 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9253 {
9254 colShare = tableWidthMinusPadding / m_colCount;
9255 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9256 for (i = 0; i < m_colCount; i++)
9257 {
9258 colWidths[i] = 0;
9259 minColWidths[i] = 0;
9260 }
9261 }
9262
9263 // We have to adjust the columns if either we need to shrink the
9264 // table to fit the parent/table width, or we explicitly set the
9265 // table width and need to stretch out the table.
9266 if (widthLeft < 0 || stretchToFitTableWidth)
9267 {
9268 for (i = 0; i < m_colCount; i++)
9269 {
9270 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9271 {
9272 if (minColWidths[i] > 0)
9273 colWidths[i] = minColWidths[i] + colShare;
9274 else
9275 colWidths[i] = colShare;
9276 if (i == (m_colCount-1))
9277 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9278 }
9279 }
9280 }
9281
9282 // TODO: if spanned cells have no specified or max width, make them the
9283 // as big as the columns they span. Do this for all spanned cells in all
9284 // rows, of course. Size any spanned cells left over at the end - even if they
9285 // have width > 0, make sure they're limited to the appropriate column edge.
9286
9287
9288/*
9289 Sort out confusion between content width
9290 and overall width later. For now, assume we specify overall width.
9291
9292 So, now we've laid out the table to fit into the given space
9293 and have used specified widths and minimum widths.
9294
9295 Now we need to consider how we will try to take maximum width into account.
9296
9297*/
9298
9299 // (??) TODO: take max width into account
9300
9301 // (6) Lay out all cells again with the current values
9302
9303 int maxRight = 0;
9304 int y = availableSpace.y;
9305 for (j = 0; j < m_rowCount; j++)
9306 {
9307 int x = availableSpace.x; // TODO: take into account centering etc.
9308 int maxCellHeight = 0;
9309 int maxSpecifiedCellHeight = 0;
9310
bb7bbd12
JS
9311 wxArrayInt actualWidths;
9312 actualWidths.Add(0, m_colCount);
603f702b
JS
9313
9314 wxTextAttrDimensionConverter converter(dc, scale);
9315 for (i = 0; i < m_colCount; i++)
9316 {
9317 wxRichTextCell* cell = GetCell(j, i);
9318 if (cell->IsShown())
9319 {
603f702b
JS
9320 // Get max specified cell height
9321 // Don't handle percentages for height
9322 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9323 {
9324 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9325 if (h > maxSpecifiedCellHeight)
9326 maxSpecifiedCellHeight = h;
9327 }
9328
9329 if (colWidths[i] > 0) // absolute or proportional width has been specified
9330 {
9331 int colSpan = 1;
9332 if (cell->GetProperties().HasProperty(wxT("colspan")))
9333 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9334
9335 wxRect availableCellSpace;
9336
9337 // TODO: take into acount spans
9338 if (colSpan > 1)
9339 {
9340 // Calculate the size of this spanning cell from its constituent columns
9341 int xx = x;
9342 int spans = wxMin(colSpan, m_colCount - i);
9343 for (k = i; k < spans; k++)
9344 {
9345 if (k != i)
9346 xx += paddingX;
9347 xx += colWidths[k];
9348 }
9349 availableCellSpace = wxRect(x, y, xx, -1);
9350 }
9351 else
9352 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9353
9354 // Store actual width so we can force cell to be the appropriate width on the final loop
9355 actualWidths[i] = availableCellSpace.GetWidth();
9356
9357 // Lay out cell
9358 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9359 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9360
9361 // TODO: use GetCachedSize().x to compute 'natural' size
9362
9363 x += (availableCellSpace.GetWidth() + paddingX);
9364 if (cell->GetCachedSize().y > maxCellHeight)
9365 maxCellHeight = cell->GetCachedSize().y;
9366 }
9367 }
9368 }
9369
9370 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9371
9372 for (i = 0; i < m_colCount; i++)
9373 {
9374 wxRichTextCell* cell = GetCell(j, i);
9375 if (cell->IsShown())
9376 {
9377 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9378 // Lay out cell with new height
9379 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9380 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9381
9382 // Make sure the cell size really is the appropriate size,
9383 // not the calculated box size
9384 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9385
9386 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9387 }
9388 }
9389
9390 y += maxCellHeight;
9391 if (j < (m_rowCount-1))
9392 y += paddingY;
9393 }
9394
9395 // We need to add back the margins etc.
9396 {
9397 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9398 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 9399 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
9400 SetCachedSize(marginRect.GetSize());
9401 }
9402
9403 // TODO: calculate max size
9404 {
9405 SetMaxSize(GetCachedSize());
9406 }
9407
9408 // TODO: calculate min size
9409 {
9410 SetMinSize(GetCachedSize());
9411 }
9412
9413 // TODO: currently we use either a fixed table width or the parent's size.
9414 // We also want to be able to calculate the table width from its content,
9415 // whether using fixed column widths or cell content min/max width.
9416 // Probably need a boolean flag to say whether we need to stretch cells
9417 // to fit the table width, or to simply use min/max cell widths. The
9418 // trouble with this is that if cell widths are not specified, they
9419 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9420 // Anyway, ignoring that problem, we probably need to factor layout into a function
9421 // that can can calculate the maximum unconstrained layout in case table size is
9422 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9423 // constrain Layout(), or the previously-calculated max size to constraint layout.
9424
9425 return true;
9426}
9427
9428// Finds the absolute position and row height for the given character position
8db2e3ef 9429bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
9430{
9431 wxRichTextCell* child = GetCell(index+1);
9432 if (child)
9433 {
9434 // Find the position at the start of the child cell, since the table doesn't
9435 // have any caret position of its own.
8db2e3ef 9436 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
9437 }
9438 else
9439 return false;
9440}
9441
9442// Get the cell at the given character position (in the range of the table).
9443wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9444{
9445 int row = 0, col = 0;
9446 if (GetCellRowColumnPosition(pos, row, col))
9447 {
9448 return GetCell(row, col);
9449 }
9450 else
9451 return NULL;
9452}
9453
9454// Get the row/column for a given character position
9455bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9456{
9457 if (m_colCount == 0 || m_rowCount == 0)
9458 return false;
9459
9460 row = (int) (pos / m_colCount);
9461 col = pos - (row * m_colCount);
9462
9463 wxASSERT(row < m_rowCount && col < m_colCount);
9464
9465 if (row < m_rowCount && col < m_colCount)
9466 return true;
9467 else
9468 return false;
9469}
9470
9471// Calculate range, taking row/cell ordering into account instead of relying
9472// on list ordering.
9473void wxRichTextTable::CalculateRange(long start, long& end)
9474{
9475 long current = start;
9476 long lastEnd = current;
9477
9478 if (IsTopLevel())
9479 {
9480 current = 0;
9481 lastEnd = 0;
9482 }
9483
9484 int i, j;
9485 for (i = 0; i < m_rowCount; i++)
9486 {
9487 for (j = 0; j < m_colCount; j++)
9488 {
9489 wxRichTextCell* child = GetCell(i, j);
9490 if (child)
9491 {
9492 long childEnd = 0;
9493
9494 child->CalculateRange(current, childEnd);
9495
9496 lastEnd = childEnd;
9497 current = childEnd + 1;
9498 }
9499 }
9500 }
9501
9502 // A top-level object always has a range of size 1,
9503 // because its children don't count at this level.
9504 end = start;
9505 m_range.SetRange(start, start);
9506
9507 // An object with no children has zero length
9508 if (m_children.GetCount() == 0)
9509 lastEnd --;
9510 m_ownRange.SetRange(0, lastEnd);
9511}
9512
9513// Gets the range size.
8db2e3ef 9514bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b 9515{
8db2e3ef 9516 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
603f702b
JS
9517}
9518
9519// Deletes content in the given range.
9520bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
9521{
9522 // TODO: implement deletion of cells
9523 return true;
9524}
9525
9526// Gets any text in this object for the given range.
9527wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
9528{
9529 return wxRichTextBox::GetTextForRange(range);
9530}
9531
9532// Copies this object.
9533void wxRichTextTable::Copy(const wxRichTextTable& obj)
9534{
9535 wxRichTextBox::Copy(obj);
9536
9537 ClearTable();
9538
9539 m_rowCount = obj.m_rowCount;
9540 m_colCount = obj.m_colCount;
9541
9542 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
9543
9544 int i, j;
9545 for (i = 0; i < m_rowCount; i++)
9546 {
9547 wxRichTextObjectPtrArray& colArray = m_cells[i];
9548 for (j = 0; j < m_colCount; j++)
9549 {
9550 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
9551 AppendChild(cell);
9552
9553 colArray.Add(cell);
9554 }
9555 }
9556}
9557
9558void wxRichTextTable::ClearTable()
9559{
9560 m_cells.Clear();
9561 DeleteChildren();
9562}
9563
9564bool wxRichTextTable::CreateTable(int rows, int cols)
9565{
9566 ClearTable();
9567
9568 m_rowCount = rows;
9569 m_colCount = cols;
9570
9571 m_cells.Add(wxRichTextObjectPtrArray(), rows);
9572
9573 int i, j;
9574 for (i = 0; i < rows; i++)
9575 {
9576 wxRichTextObjectPtrArray& colArray = m_cells[i];
9577 for (j = 0; j < cols; j++)
9578 {
9579 wxRichTextCell* cell = new wxRichTextCell;
9580 AppendChild(cell);
9581 cell->AddParagraph(wxEmptyString);
9582
9583 colArray.Add(cell);
9584 }
9585 }
9586
9587 return true;
9588}
9589
9590wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
9591{
9592 wxASSERT(row < m_rowCount);
9593 wxASSERT(col < m_colCount);
9594
9595 if (row < m_rowCount && col < m_colCount)
9596 {
9597 wxRichTextObjectPtrArray& colArray = m_cells[row];
9598 wxRichTextObject* obj = colArray[col];
9599 return wxDynamicCast(obj, wxRichTextCell);
9600 }
9601 else
d67faa04 9602 return NULL;
603f702b
JS
9603}
9604
9605// Returns a selection object specifying the selections between start and end character positions.
9606// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9607wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
9608{
9609 wxRichTextSelection selection;
9610 selection.SetContainer((wxRichTextTable*) this);
9611
9612 if (start > end)
9613 {
9614 long tmp = end;
9615 end = start;
9616 start = tmp;
9617 }
9618
9619 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
9620
9621 if (end >= (m_colCount * m_rowCount))
9622 return selection;
9623
9624 // We need to find the rectangle of cells that is described by the rectangle
9625 // with start, end as the diagonal. Make sure we don't add cells that are
9626 // not currenty visible because they are overlapped by spanning cells.
9627/*
9628 --------------------------
9629 | 0 | 1 | 2 | 3 | 4 |
9630 --------------------------
9631 | 5 | 6 | 7 | 8 | 9 |
9632 --------------------------
9633 | 10 | 11 | 12 | 13 | 14 |
9634 --------------------------
9635 | 15 | 16 | 17 | 18 | 19 |
9636 --------------------------
9637
9638 Let's say we select 6 -> 18.
9639
9640 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9641 which is left and which is right.
9642
9643 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9644
9645 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9646 and (b) shown.
9647
9648
9649*/
9650
9651 int leftCol = start - m_colCount * int(start/m_colCount);
9652 int rightCol = end - m_colCount * int(end/m_colCount);
9653
9654 int topRow = int(start/m_colCount);
9655 int bottomRow = int(end/m_colCount);
9656
9657 if (leftCol > rightCol)
9658 {
9659 int tmp = rightCol;
9660 rightCol = leftCol;
9661 leftCol = tmp;
9662 }
9663
9664 if (topRow > bottomRow)
9665 {
9666 int tmp = bottomRow;
9667 bottomRow = topRow;
9668 topRow = tmp;
9669 }
9670
9671 int i, j;
9672 for (i = topRow; i <= bottomRow; i++)
9673 {
9674 for (j = leftCol; j <= rightCol; j++)
9675 {
9676 wxRichTextCell* cell = GetCell(i, j);
9677 if (cell && cell->IsShown())
9678 selection.Add(cell->GetRange());
9679 }
9680 }
9681
9682 return selection;
9683}
9684
9685// Sets the attributes for the cells specified by the selection.
9686bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
9687{
9688 if (selection.GetContainer() != this)
9689 return false;
9690
9691 wxRichTextBuffer* buffer = GetBuffer();
9692 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
9693 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
9694
9695 if (withUndo)
9696 buffer->BeginBatchUndo(_("Set Cell Style"));
9697
9698 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
9699 while (node)
9700 {
9701 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
9702 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
9703 SetStyle(cell, style, flags);
9704 node = node->GetNext();
9705 }
9706
9707 // Do action, or delay it until end of batch.
9708 if (withUndo)
9709 buffer->EndBatchUndo();
9710
9711 return true;
9712}
9713
9714bool wxRichTextTable::DeleteRows(int startRow, int noRows)
9715{
9716 wxASSERT((startRow + noRows) < m_rowCount);
9717 if ((startRow + noRows) >= m_rowCount)
9718 return false;
9719
9720 int i, j;
9721 for (i = startRow; i < (startRow+noRows); i++)
9722 {
9723 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
9724 for (j = 0; j < (int) colArray.GetCount(); j++)
9725 {
9726 wxRichTextObject* cell = colArray[j];
9727 RemoveChild(cell, true);
9728 }
9729
9730 // Keep deleting at the same position, since we move all
9731 // the others up
9732 m_cells.RemoveAt(startRow);
9733 }
9734
9735 m_rowCount = m_rowCount - noRows;
9736
9737 return true;
9738}
9739
9740bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
9741{
9742 wxASSERT((startCol + noCols) < m_colCount);
9743 if ((startCol + noCols) >= m_colCount)
9744 return false;
9745
9746 bool deleteRows = (noCols == m_colCount);
9747
9748 int i, j;
9749 for (i = 0; i < m_rowCount; i++)
9750 {
9751 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
9752 for (j = startCol; j < (startCol+noCols); j++)
9753 {
9754 wxRichTextObject* cell = colArray[j];
9755 RemoveChild(cell, true);
9756 }
9757
9758 if (deleteRows)
9759 m_cells.RemoveAt(0);
9760 }
9761
9762 if (deleteRows)
9763 m_rowCount = 0;
9764 m_colCount = m_colCount - noCols;
9765
9766 return true;
9767}
9768
9769bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
9770{
9771 wxASSERT(startRow <= m_rowCount);
9772 if (startRow > m_rowCount)
9773 return false;
9774
9775 int i, j;
9776 for (i = 0; i < noRows; i++)
9777 {
9778 int idx;
9779 if (startRow == m_rowCount)
9780 {
9781 m_cells.Add(wxRichTextObjectPtrArray());
9782 idx = m_cells.GetCount() - 1;
9783 }
9784 else
9785 {
9786 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
9787 idx = startRow+i;
9788 }
9789
9790 wxRichTextObjectPtrArray& colArray = m_cells[idx];
9791 for (j = 0; j < m_colCount; j++)
9792 {
9793 wxRichTextCell* cell = new wxRichTextCell;
9794 cell->GetAttributes() = attr;
9795
9796 AppendChild(cell);
9797 colArray.Add(cell);
9798 }
9799 }
9800
9801 m_rowCount = m_rowCount + noRows;
9802 return true;
9803}
9804
9805bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
9806{
9807 wxASSERT(startCol <= m_colCount);
9808 if (startCol > m_colCount)
9809 return false;
9810
9811 int i, j;
9812 for (i = 0; i < m_rowCount; i++)
9813 {
9814 wxRichTextObjectPtrArray& colArray = m_cells[i];
9815 for (j = 0; j < noCols; j++)
9816 {
9817 wxRichTextCell* cell = new wxRichTextCell;
9818 cell->GetAttributes() = attr;
9819
9820 AppendChild(cell);
9821
9822 if (startCol == m_colCount)
9823 colArray.Add(cell);
9824 else
9825 colArray.Insert(cell, startCol+j);
9826 }
9827 }
9828
9829 m_colCount = m_colCount + noCols;
9830
9831 return true;
5ad9ae3a
JS
9832}
9833
603f702b
JS
9834// Edit properties via a GUI
9835bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 9836{
603f702b
JS
9837 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
9838 boxDlg.SetAttributes(GetAttributes());
9839
9840 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 9841 {
603f702b
JS
9842 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9843 return true;
5ad9ae3a
JS
9844 }
9845 else
9846 return false;
bec80f4f
JS
9847}
9848
5d7836c4
JS
9849/*
9850 * Module to initialise and clean up handlers
9851 */
9852
9853class wxRichTextModule: public wxModule
9854{
9855DECLARE_DYNAMIC_CLASS(wxRichTextModule)
9856public:
9857 wxRichTextModule() {}
cfa3b256
JS
9858 bool OnInit()
9859 {
d2d0adc7 9860 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
9861 wxRichTextBuffer::InitStandardHandlers();
9862 wxRichTextParagraph::InitDefaultTabs();
1aca9fcd
JS
9863
9864 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9865 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9866 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9867 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9868 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9869 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9870 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9871 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9872 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9873
cfa3b256 9874 return true;
47b378bd 9875 }
cfa3b256
JS
9876 void OnExit()
9877 {
9878 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 9879 wxRichTextBuffer::CleanUpDrawingHandlers();
7c9fdebe 9880 wxRichTextBuffer::CleanUpFieldTypes();
1aca9fcd 9881 wxRichTextXMLHandler::ClearNodeToClassMap();
cfa3b256
JS
9882 wxRichTextDecimalToRoman(-1);
9883 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 9884 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 9885 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 9886 }
5d7836c4
JS
9887};
9888
9889IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
9890
9891
f1d6804f
RD
9892// If the richtext lib is dynamically loaded after the app has already started
9893// (such as from wxPython) then the built-in module system will not init this
9894// module. Provide this function to do it manually.
9895void wxRichTextModuleInit()
9896{
9897 wxModule* module = new wxRichTextModule;
9898 module->Init();
9899 wxModule::RegisterModule(module);
9900}
9901
9902
5d7836c4
JS
9903/*!
9904 * Commands for undo/redo
9905 *
9906 */
9907
9908wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 9909 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 9910{
603f702b 9911 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
9912}
9913
7fe8059f 9914wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
9915{
9916}
9917
9918wxRichTextCommand::~wxRichTextCommand()
9919{
9920 ClearActions();
9921}
9922
9923void wxRichTextCommand::AddAction(wxRichTextAction* action)
9924{
9925 if (!m_actions.Member(action))
9926 m_actions.Append(action);
9927}
9928
9929bool wxRichTextCommand::Do()
9930{
09f14108 9931 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
9932 {
9933 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
9934 action->Do();
9935 }
9936
9937 return true;
9938}
9939
9940bool wxRichTextCommand::Undo()
9941{
09f14108 9942 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
9943 {
9944 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
9945 action->Undo();
9946 }
9947
9948 return true;
9949}
9950
9951void wxRichTextCommand::ClearActions()
9952{
9953 WX_CLEAR_LIST(wxList, m_actions);
9954}
9955
9956/*!
9957 * Individual action
9958 *
9959 */
9960
603f702b
JS
9961wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
9962 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
9963 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
9964{
9965 m_buffer = buffer;
603f702b
JS
9966 m_object = NULL;
9967 m_containerAddress.Create(buffer, container);
5d7836c4
JS
9968 m_ignoreThis = ignoreFirstTime;
9969 m_cmdId = id;
9970 m_position = -1;
9971 m_ctrl = ctrl;
9972 m_name = name;
9973 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
9974 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
9975 if (cmd)
9976 cmd->AddAction(this);
9977}
9978
9979wxRichTextAction::~wxRichTextAction()
9980{
603f702b
JS
9981 if (m_object)
9982 delete m_object;
9983}
9984
9985// Returns the container that this action refers to, using the container address and top-level buffer.
9986wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
9987{
9988 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
9989 return container;
5d7836c4
JS
9990}
9991
603f702b 9992
7051fa41
JS
9993void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
9994{
9995 // Store a list of line start character and y positions so we can figure out which area
9996 // we need to refresh
9997
9998#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
9999 wxRichTextParagraphLayoutBox* container = GetContainer();
10000 wxASSERT(container != NULL);
10001 if (!container)
10002 return;
10003
7051fa41
JS
10004 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10005 // If we had several actions, which only invalidate and leave layout until the
10006 // paint handler is called, then this might not be true. So we may need to switch
10007 // optimisation on only when we're simply adding text and not simultaneously
10008 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10009 // first, but of course this means we'll be doing it twice.
603f702b 10010 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41
JS
10011 {
10012 wxSize clientSize = m_ctrl->GetClientSize();
10013 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
10014 int lastY = firstVisiblePt.y + clientSize.y;
10015
603f702b
JS
10016 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10017 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
10018 while (node)
10019 {
10020 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10021 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10022 while (node2)
10023 {
10024 wxRichTextLine* line = node2->GetData();
10025 wxPoint pt = line->GetAbsolutePosition();
10026 wxRichTextRange range = line->GetAbsoluteRange();
10027
10028 if (pt.y > lastY)
10029 {
10030 node2 = wxRichTextLineList::compatibility_iterator();
10031 node = wxRichTextObjectList::compatibility_iterator();
10032 }
10033 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10034 {
10035 optimizationLineCharPositions.Add(range.GetStart());
10036 optimizationLineYPositions.Add(pt.y);
10037 }
10038
10039 if (node2)
10040 node2 = node2->GetNext();
10041 }
10042
10043 if (node)
10044 node = node->GetNext();
10045 }
10046 }
10047#endif
10048}
10049
5d7836c4
JS
10050bool wxRichTextAction::Do()
10051{
10052 m_buffer->Modify(true);
10053
603f702b
JS
10054 wxRichTextParagraphLayoutBox* container = GetContainer();
10055 wxASSERT(container != NULL);
10056 if (!container)
10057 return false;
10058
5d7836c4
JS
10059 switch (m_cmdId)
10060 {
10061 case wxRICHTEXT_INSERT:
10062 {
ea160b2e
JS
10063 // Store a list of line start character and y positions so we can figure out which area
10064 // we need to refresh
10065 wxArrayInt optimizationLineCharPositions;
10066 wxArrayInt optimizationLineYPositions;
10067
10068#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 10069 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
10070#endif
10071
603f702b
JS
10072 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10073 container->UpdateRanges();
10074
10075 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10076 // Layout() would stop prematurely at the top level.
10077 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 10078
603f702b 10079 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
10080
10081 // Character position to caret position
10082 newCaretPosition --;
10083
10084 // Don't take into account the last newline
5d7836c4
JS
10085 if (m_newParagraphs.GetPartialParagraph())
10086 newCaretPosition --;
46ee0e5b 10087 else
7c081bd2 10088 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
10089 {
10090 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10091 if (p->GetRange().GetLength() == 1)
10092 newCaretPosition --;
10093 }
5d7836c4 10094
603f702b 10095 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 10096
7051fa41 10097 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 10098
5912d19e
JS
10099 wxRichTextEvent cmdEvent(
10100 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10101 m_ctrl ? m_ctrl->GetId() : -1);
10102 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10103 cmdEvent.SetRange(GetRange());
10104 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10105 cmdEvent.SetContainer(container);
3e541562 10106
5912d19e 10107 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
10108
10109 break;
10110 }
10111 case wxRICHTEXT_DELETE:
10112 {
7051fa41
JS
10113 wxArrayInt optimizationLineCharPositions;
10114 wxArrayInt optimizationLineYPositions;
10115
10116#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10117 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10118#endif
10119
603f702b
JS
10120 container->DeleteRange(GetRange());
10121 container->UpdateRanges();
10122 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10123 // Layout() would stop prematurely at the top level.
10124 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 10125
6ccbca24 10126 long caretPos = GetRange().GetStart()-1;
603f702b 10127 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
10128 caretPos --;
10129
7051fa41 10130 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 10131
5912d19e
JS
10132 wxRichTextEvent cmdEvent(
10133 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10134 m_ctrl ? m_ctrl->GetId() : -1);
10135 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10136 cmdEvent.SetRange(GetRange());
10137 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10138 cmdEvent.SetContainer(container);
3e541562 10139
5912d19e
JS
10140 m_buffer->SendEvent(cmdEvent);
10141
5d7836c4
JS
10142 break;
10143 }
10144 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10145 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10146 {
10147 ApplyParagraphs(GetNewParagraphs());
603f702b
JS
10148
10149 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10150 // Layout() would stop prematurely at the top level.
10151 container->InvalidateHierarchy(GetRange());
10152
10153 UpdateAppearance(GetPosition());
10154
10155 wxRichTextEvent cmdEvent(
590a0f8b 10156 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
10157 m_ctrl ? m_ctrl->GetId() : -1);
10158 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10159 cmdEvent.SetRange(GetRange());
10160 cmdEvent.SetPosition(GetRange().GetStart());
10161 cmdEvent.SetContainer(container);
10162
10163 m_buffer->SendEvent(cmdEvent);
10164
10165 break;
10166 }
10167 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10168 {
10169 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10170 if (obj)
10171 {
10172 wxRichTextAttr oldAttr = obj->GetAttributes();
10173 obj->GetAttributes() = m_attributes;
10174 m_attributes = oldAttr;
10175 }
10176
10177 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10178 // Layout() would stop prematurely at the top level.
10179 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10180
10181 UpdateAppearance(GetPosition());
10182
5912d19e
JS
10183 wxRichTextEvent cmdEvent(
10184 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10185 m_ctrl ? m_ctrl->GetId() : -1);
10186 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10187 cmdEvent.SetRange(GetRange());
10188 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10189 cmdEvent.SetContainer(container);
3e541562 10190
5912d19e
JS
10191 m_buffer->SendEvent(cmdEvent);
10192
603f702b
JS
10193 break;
10194 }
10195 case wxRICHTEXT_CHANGE_OBJECT:
10196 {
10197 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10198 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10199 if (obj && m_object)
10200 {
10201 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10202 if (node)
10203 {
10204 wxRichTextObject* obj = node->GetData();
10205 node->SetData(m_object);
10206 m_object = obj;
10207 }
10208 }
10209
10210 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10211 // Layout() would stop prematurely at the top level.
10212 container->InvalidateHierarchy(GetRange());
10213
10214 UpdateAppearance(GetPosition());
10215
10216 // TODO: send new kind of modification event
10217
5d7836c4
JS
10218 break;
10219 }
10220 default:
10221 break;
10222 }
10223
10224 return true;
10225}
10226
10227bool wxRichTextAction::Undo()
10228{
10229 m_buffer->Modify(true);
10230
603f702b
JS
10231 wxRichTextParagraphLayoutBox* container = GetContainer();
10232 wxASSERT(container != NULL);
10233 if (!container)
10234 return false;
10235
5d7836c4
JS
10236 switch (m_cmdId)
10237 {
10238 case wxRICHTEXT_INSERT:
10239 {
7051fa41
JS
10240 wxArrayInt optimizationLineCharPositions;
10241 wxArrayInt optimizationLineYPositions;
10242
10243#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10244 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10245#endif
10246
603f702b
JS
10247 container->DeleteRange(GetRange());
10248 container->UpdateRanges();
7c9fdebe 10249
603f702b
JS
10250 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10251 // Layout() would stop prematurely at the top level.
10252 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
10253
10254 long newCaretPosition = GetPosition() - 1;
3e541562 10255
7051fa41 10256 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10257
5912d19e
JS
10258 wxRichTextEvent cmdEvent(
10259 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10260 m_ctrl ? m_ctrl->GetId() : -1);
10261 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10262 cmdEvent.SetRange(GetRange());
10263 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10264 cmdEvent.SetContainer(container);
3e541562 10265
5912d19e
JS
10266 m_buffer->SendEvent(cmdEvent);
10267
5d7836c4
JS
10268 break;
10269 }
10270 case wxRICHTEXT_DELETE:
10271 {
7051fa41
JS
10272 wxArrayInt optimizationLineCharPositions;
10273 wxArrayInt optimizationLineYPositions;
10274
10275#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10276 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10277#endif
10278
603f702b
JS
10279 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10280 container->UpdateRanges();
7c9fdebe 10281
603f702b
JS
10282 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10283 // Layout() would stop prematurely at the top level.
10284 container->InvalidateHierarchy(GetRange());
5d7836c4 10285
7051fa41 10286 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10287
5912d19e
JS
10288 wxRichTextEvent cmdEvent(
10289 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10290 m_ctrl ? m_ctrl->GetId() : -1);
10291 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10292 cmdEvent.SetRange(GetRange());
10293 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10294 cmdEvent.SetContainer(container);
3e541562 10295
5912d19e
JS
10296 m_buffer->SendEvent(cmdEvent);
10297
5d7836c4
JS
10298 break;
10299 }
10300 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10301 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10302 {
10303 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
10304 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10305 // Layout() would stop prematurely at the top level.
10306 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10307
10308 UpdateAppearance(GetPosition());
10309
5912d19e 10310 wxRichTextEvent cmdEvent(
590a0f8b 10311 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
10312 m_ctrl ? m_ctrl->GetId() : -1);
10313 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10314 cmdEvent.SetRange(GetRange());
10315 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10316 cmdEvent.SetContainer(container);
3e541562 10317
5912d19e
JS
10318 m_buffer->SendEvent(cmdEvent);
10319
5d7836c4
JS
10320 break;
10321 }
603f702b
JS
10322 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10323 case wxRICHTEXT_CHANGE_OBJECT:
10324 {
10325 return Do();
10326 }
5d7836c4
JS
10327 default:
10328 break;
10329 }
10330
10331 return true;
10332}
10333
10334/// Update the control appearance
603f702b 10335void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 10336{
603f702b
JS
10337 wxRichTextParagraphLayoutBox* container = GetContainer();
10338 wxASSERT(container != NULL);
10339 if (!container)
10340 return;
10341
5d7836c4
JS
10342 if (m_ctrl)
10343 {
603f702b 10344 m_ctrl->SetFocusObject(container);
5d7836c4 10345 m_ctrl->SetCaretPosition(caretPosition);
603f702b 10346
5d7836c4
JS
10347 if (!m_ctrl->IsFrozen())
10348 {
603f702b
JS
10349 wxRect containerRect = container->GetRect();
10350
2f36e8dc 10351 m_ctrl->LayoutContent();
5d7836c4 10352
603f702b
JS
10353 // Refresh everything if there were floating objects or the container changed size
10354 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10355 if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
10356 {
10357 m_ctrl->Refresh(false);
10358 }
10359 else
10360
10361#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10362 // Find refresh rectangle if we are in a position to optimise refresh
10363 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10364 {
10365 size_t i;
10366
10367 wxSize clientSize = m_ctrl->GetClientSize();
10368 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
10369
10370 // Start/end positions
10371 int firstY = 0;
10372 int lastY = firstVisiblePt.y + clientSize.y;
10373
10374 bool foundEnd = false;
10375
10376 // position offset - how many characters were inserted
10377 int positionOffset = GetRange().GetLength();
10378
10379 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10380 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10381 positionOffset = - positionOffset;
10382
10383 // find the first line which is being drawn at the same position as it was
10384 // before. Since we're talking about a simple insertion, we can assume
10385 // that the rest of the window does not need to be redrawn.
10386
10387 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10388 // Since we support floating layout, we should redraw the whole para instead of just
10389 // the first line touching the invalid range.
10390 if (para)
10391 {
10392 firstY = para->GetPosition().y;
10393 }
10394
10395 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10396 while (node)
10397 {
10398 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10399 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10400 while (node2)
10401 {
10402 wxRichTextLine* line = node2->GetData();
10403 wxPoint pt = line->GetAbsolutePosition();
10404 wxRichTextRange range = line->GetAbsoluteRange();
10405
10406 // we want to find the first line that is in the same position
10407 // as before. This will mean we're at the end of the changed text.
10408
10409 if (pt.y > lastY) // going past the end of the window, no more info
10410 {
10411 node2 = wxRichTextLineList::compatibility_iterator();
10412 node = wxRichTextObjectList::compatibility_iterator();
10413 }
10414 // Detect last line in the buffer
10415 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10416 {
10417 // If deleting text, make sure we refresh below as well as above
10418 if (positionOffset >= 0)
10419 {
10420 foundEnd = true;
10421 lastY = pt.y + line->GetSize().y;
10422 }
10423
10424 node2 = wxRichTextLineList::compatibility_iterator();
10425 node = wxRichTextObjectList::compatibility_iterator();
10426
10427 break;
10428 }
10429 else
10430 {
10431 // search for this line being at the same position as before
10432 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10433 {
10434 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10435 ((*optimizationLineYPositions)[i] == pt.y))
10436 {
10437 // Stop, we're now the same as we were
10438 foundEnd = true;
10439
10440 lastY = pt.y;
10441
10442 node2 = wxRichTextLineList::compatibility_iterator();
10443 node = wxRichTextObjectList::compatibility_iterator();
10444
10445 break;
10446 }
10447 }
10448 }
10449
10450 if (node2)
10451 node2 = node2->GetNext();
10452 }
10453
10454 if (node)
10455 node = node->GetNext();
10456 }
10457
10458 firstY = wxMax(firstVisiblePt.y, firstY);
10459 if (!foundEnd)
10460 lastY = firstVisiblePt.y + clientSize.y;
10461
10462 // Convert to device coordinates
10463 wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY));
10464 m_ctrl->RefreshRect(rect);
10465 }
10466 else
1c13f06e 10467#endif
603f702b
JS
10468 m_ctrl->Refresh(false);
10469
10470 m_ctrl->PositionCaret();
4fe83b93
JS
10471
10472 // This causes styles to persist when doing programmatic
10473 // content creation except when Freeze/Thaw is used, so
10474 // disable this and check for the consequences.
10475 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 10476
5d7836c4 10477 if (sendUpdateEvent)
0ec1179b 10478 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 10479 }
7fe8059f 10480 }
5d7836c4
JS
10481}
10482
10483/// Replace the buffer paragraphs with the new ones.
0ca07313 10484void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 10485{
603f702b
JS
10486 wxRichTextParagraphLayoutBox* container = GetContainer();
10487 wxASSERT(container != NULL);
10488 if (!container)
10489 return;
10490
5d7836c4
JS
10491 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
10492 while (node)
10493 {
10494 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
10495 wxASSERT (para != NULL);
10496
10497 // We'll replace the existing paragraph by finding the paragraph at this position,
10498 // delete its node data, and setting a copy as the new node data.
10499 // TODO: make more efficient by simply swapping old and new paragraph objects.
10500
603f702b 10501 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
10502 if (existingPara)
10503 {
603f702b 10504 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
10505 if (bufferParaNode)
10506 {
10507 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 10508 newPara->SetParent(container);
5d7836c4
JS
10509
10510 bufferParaNode->SetData(newPara);
10511
10512 delete existingPara;
10513 }
10514 }
10515
10516 node = node->GetNext();
10517 }
10518}
10519
10520
10521/*!
10522 * wxRichTextRange
10523 * This stores beginning and end positions for a range of data.
10524 */
10525
603f702b
JS
10526WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
10527
5d7836c4
JS
10528/// Limit this range to be within 'range'
10529bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
10530{
10531 if (m_start < range.m_start)
10532 m_start = range.m_start;
10533
10534 if (m_end > range.m_end)
10535 m_end = range.m_end;
10536
10537 return true;
10538}
10539
10540/*!
10541 * wxRichTextImage implementation
10542 * This object represents an image.
10543 */
10544
bec80f4f 10545IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 10546
24777478 10547wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 10548 wxRichTextObject(parent)
5d7836c4 10549{
23698b12 10550 Init();
cdaed652 10551 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
10552 if (charStyle)
10553 SetAttributes(*charStyle);
5d7836c4
JS
10554}
10555
24777478 10556wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 10557 wxRichTextObject(parent)
5d7836c4 10558{
23698b12 10559 Init();
5d7836c4 10560 m_imageBlock = imageBlock;
4f32b3cf
JS
10561 if (charStyle)
10562 SetAttributes(*charStyle);
5d7836c4
JS
10563}
10564
23698b12
JS
10565void wxRichTextImage::Init()
10566{
10567 m_originalImageSize = wxSize(-1, -1);
10568}
10569
cdaed652
VZ
10570/// Create a cached image at the required size
10571bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
5d7836c4 10572{
23698b12
JS
10573 if (!m_imageBlock.IsOk())
10574 return false;
10575
10576 // If we have an original image size, use that to compute the cached bitmap size
10577 // instead of loading the image each time. This way we can avoid loading
10578 // the image so long as the new cached bitmap size hasn't changed.
10579
10580 wxImage image;
10581 if (resetCache || m_originalImageSize == wxSize(-1, -1))
cdaed652 10582 {
23698b12 10583 m_imageCache = wxNullBitmap;
ce00f59b 10584
cdaed652
VZ
10585 m_imageBlock.Load(image);
10586 if (!image.IsOk())
10587 return false;
ce00f59b 10588
23698b12
JS
10589 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
10590 }
10591
10592 int width = m_originalImageSize.GetWidth();
10593 int height = m_originalImageSize.GetHeight();
10594
10595 int parentWidth = 0;
10596 int parentHeight = 0;
bec80f4f 10597
23698b12
JS
10598 int maxWidth = -1;
10599 int maxHeight = -1;
10600
10601 wxRichTextBuffer* buffer = GetBuffer();
10602 if (buffer)
10603 {
10604 wxSize sz;
10605 if (buffer->GetRichTextCtrl())
cdaed652 10606 {
23698b12
JS
10607 // Subtract borders
10608 sz = buffer->GetRichTextCtrl()->GetClientSize();
10609
10610 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10611 marginRect = wxRect(0, 0, sz.x, sz.y);
10612 buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
10613
10614 sz = contentRect.GetSize();
10615
10616 // Start with a maximum width of the control size, even if not specified by the content,
10617 // to minimize the amount of picture overlapping the right-hand side
10618 maxWidth = sz.x;
cdaed652 10619 }
23698b12
JS
10620 else
10621 sz = buffer->GetCachedSize();
10622 parentWidth = sz.GetWidth();
10623 parentHeight = sz.GetHeight();
10624 }
10625
10626 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10627 {
10628 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10629 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
10630 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10631 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10632 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10633 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10634 }
10635
10636 // Limit to max width
10637
10638 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10639 {
10640 int mw = -1;
10641
10642 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10643 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
10644 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10645 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10646 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10647 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10648
10649 // If we already have a smaller max width due to the constraints of the control size,
10650 // don't use the larger max width.
10651 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
10652 maxWidth = mw;
10653 }
10654
10655 if (maxWidth > 0 && width > maxWidth)
10656 width = maxWidth;
10657
10658 // Preserve the aspect ratio
10659 if (width != m_originalImageSize.GetWidth())
10660 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
10661
10662 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10663 {
10664 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10665 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
10666 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10667 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10668 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10669 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10670
10671 // Preserve the aspect ratio
10672 if (height != m_originalImageSize.GetHeight())
10673 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
10674 }
10675
10676 // Limit to max height
10677
10678 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10679 {
10680 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10681 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
10682 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10683 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10684 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10685 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10686 }
10687
10688 if (maxHeight > 0 && height > maxHeight)
10689 {
10690 height = maxHeight;
10691
10692 // Preserve the aspect ratio
10693 if (height != m_originalImageSize.GetHeight())
10694 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
10695 }
10696
10697 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
10698 {
10699 // Do nothing, we didn't need to change the image cache
10700 }
10701 else
10702 {
10703 if (!image.IsOk())
cdaed652 10704 {
23698b12
JS
10705 m_imageBlock.Load(image);
10706 if (!image.IsOk())
10707 return false;
cdaed652 10708 }
5d7836c4 10709
cdaed652
VZ
10710 if (image.GetWidth() == width && image.GetHeight() == height)
10711 m_imageCache = wxBitmap(image);
10712 else
10713 {
10714 // If the original width and height is small, e.g. 400 or below,
10715 // scale up and then down to improve image quality. This can make
10716 // a big difference, with not much performance hit.
10717 int upscaleThreshold = 400;
10718 wxImage img;
10719 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
10720 {
10721 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
10722 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
10723 }
10724 else
10725 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
10726 m_imageCache = wxBitmap(img);
10727 }
10728 }
ce00f59b 10729
cdaed652 10730 return m_imageCache.IsOk();
5d7836c4
JS
10731}
10732
5d7836c4 10733/// Draw the item
20d09da5 10734bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 10735{
603f702b
JS
10736 if (!IsShown())
10737 return true;
10738
cdaed652
VZ
10739 // Don't need cached size AFAIK
10740 // wxSize size = GetCachedSize();
10741 if (!LoadImageCache(dc))
5d7836c4 10742 return false;
ce00f59b 10743
8db2e3ef
JS
10744 wxRichTextAttr attr(GetAttributes());
10745 context.ApplyVirtualAttributes(attr, this);
10746
10747 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b 10748
603f702b
JS
10749 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10750 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10751 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 10752 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10753
10754 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 10755
a70eb13e 10756 if (selection.WithinSelection(GetRange().GetStart(), this))
5d7836c4 10757 {
ecb5fbf1
JS
10758 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
10759 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 10760 dc.SetLogicalFunction(wxINVERT);
603f702b 10761 dc.DrawRectangle(contentRect);
5d7836c4
JS
10762 dc.SetLogicalFunction(wxCOPY);
10763 }
10764
10765 return true;
10766}
10767
10768/// Lay the item out
8db2e3ef 10769bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 10770{
cdaed652
VZ
10771 if (!LoadImageCache(dc))
10772 return false;
5d7836c4 10773
603f702b
JS
10774 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10775 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10776 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
10777
10778 wxRichTextAttr attr(GetAttributes());
10779 context.ApplyVirtualAttributes(attr, this);
10780
10781 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10782
10783 wxSize overallSize = marginRect.GetSize();
10784
10785 SetCachedSize(overallSize);
10786 SetMaxSize(overallSize);
10787 SetMinSize(overallSize);
cdaed652 10788 SetPosition(rect.GetPosition());
5d7836c4
JS
10789
10790 return true;
10791}
10792
10793/// Get/set the object size for the given range. Returns false if the range
10794/// is invalid for this object.
8db2e3ef 10795bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
5d7836c4
JS
10796{
10797 if (!range.IsWithin(GetRange()))
10798 return false;
10799
cdaed652 10800 if (!((wxRichTextImage*)this)->LoadImageCache(dc))
31778480 10801 {
cdaed652
VZ
10802 size.x = 0; size.y = 0;
10803 if (partialExtents)
31778480 10804 partialExtents->Add(0);
cdaed652 10805 return false;
31778480 10806 }
ce00f59b 10807
8db2e3ef
JS
10808 wxRichTextAttr attr(GetAttributes());
10809 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
10810
603f702b
JS
10811 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10812 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10813 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef 10814 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10815
10816 wxSize overallSize = marginRect.GetSize();
31778480 10817
cdaed652 10818 if (partialExtents)
603f702b 10819 partialExtents->Add(overallSize.x);
5d7836c4 10820
603f702b 10821 size = overallSize;
5d7836c4
JS
10822
10823 return true;
10824}
10825
603f702b
JS
10826// Get the 'natural' size for an object. For an image, it would be the
10827// image size.
10828wxTextAttrSize wxRichTextImage::GetNaturalSize() const
10829{
10830 wxTextAttrSize size;
10831 if (GetImageCache().IsOk())
10832 {
10833 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
10834 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
10835 }
10836 return size;
10837}
10838
10839
5d7836c4
JS
10840/// Copy
10841void wxRichTextImage::Copy(const wxRichTextImage& obj)
10842{
bec80f4f 10843 wxRichTextObject::Copy(obj);
59509217 10844
5d7836c4 10845 m_imageBlock = obj.m_imageBlock;
23698b12 10846 m_originalImageSize = obj.m_originalImageSize;
5d7836c4
JS
10847}
10848
cdaed652
VZ
10849/// Edit properties via a GUI
10850bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10851{
603f702b
JS
10852 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
10853 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
10854
10855 if (imageDlg.ShowModal() == wxID_OK)
10856 {
603f702b
JS
10857 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10858 // indeterminate in the object.
10859 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
10860 return true;
10861 }
10862 else
10863 return false;
10864}
10865
5d7836c4
JS
10866/*!
10867 * Utilities
10868 *
10869 */
10870
10871/// Compare two attribute objects
24777478 10872bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 10873{
38f833b1 10874 return (attr1 == attr2);
5d7836c4
JS
10875}
10876
44cc96a8 10877// Partial equality test taking flags into account
24777478 10878bool wxTextAttrEqPartial(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
44cc96a8 10879{
24777478 10880 return attr1.EqPartial(attr2);
44cc96a8 10881}
5d7836c4 10882
44cc96a8
JS
10883/// Compare tabs
10884bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
10885{
10886 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
10887 return false;
10888
44cc96a8
JS
10889 size_t i;
10890 for (i = 0; i < tabs1.GetCount(); i++)
10891 {
10892 if (tabs1[i] != tabs2[i])
10893 return false;
10894 }
10895 return true;
10896}
5d7836c4 10897
24777478 10898bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
10899{
10900 return destStyle.Apply(style, compareWith);
10901}
5d7836c4 10902
44cc96a8 10903// Remove attributes
24777478 10904bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 10905{
24777478 10906 return destStyle.RemoveStyle(style);
44cc96a8 10907}
5d7836c4 10908
44cc96a8
JS
10909/// Combine two bitlists, specifying the bits of interest with separate flags.
10910bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
10911{
24777478 10912 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 10913}
5d7836c4 10914
44cc96a8
JS
10915/// Compare two bitlists
10916bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
10917{
24777478 10918 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 10919}
38f833b1 10920
44cc96a8 10921/// Split into paragraph and character styles
24777478 10922bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 10923{
24777478 10924 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 10925}
5d7836c4 10926
44cc96a8
JS
10927/// Convert a decimal to Roman numerals
10928wxString wxRichTextDecimalToRoman(long n)
10929{
10930 static wxArrayInt decimalNumbers;
10931 static wxArrayString romanNumbers;
5d7836c4 10932
44cc96a8
JS
10933 // Clean up arrays
10934 if (n == -1)
10935 {
10936 decimalNumbers.Clear();
10937 romanNumbers.Clear();
10938 return wxEmptyString;
10939 }
5d7836c4 10940
44cc96a8
JS
10941 if (decimalNumbers.GetCount() == 0)
10942 {
10943 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 10944
44cc96a8
JS
10945 wxRichTextAddDecRom(1000, wxT("M"));
10946 wxRichTextAddDecRom(900, wxT("CM"));
10947 wxRichTextAddDecRom(500, wxT("D"));
10948 wxRichTextAddDecRom(400, wxT("CD"));
10949 wxRichTextAddDecRom(100, wxT("C"));
10950 wxRichTextAddDecRom(90, wxT("XC"));
10951 wxRichTextAddDecRom(50, wxT("L"));
10952 wxRichTextAddDecRom(40, wxT("XL"));
10953 wxRichTextAddDecRom(10, wxT("X"));
10954 wxRichTextAddDecRom(9, wxT("IX"));
10955 wxRichTextAddDecRom(5, wxT("V"));
10956 wxRichTextAddDecRom(4, wxT("IV"));
10957 wxRichTextAddDecRom(1, wxT("I"));
10958 }
5d7836c4 10959
44cc96a8
JS
10960 int i = 0;
10961 wxString roman;
ea160b2e 10962
44cc96a8 10963 while (n > 0 && i < 13)
42688aea 10964 {
44cc96a8
JS
10965 if (n >= decimalNumbers[i])
10966 {
10967 n -= decimalNumbers[i];
10968 roman += romanNumbers[i];
10969 }
10970 else
10971 {
10972 i ++;
10973 }
42688aea 10974 }
44cc96a8
JS
10975 if (roman.IsEmpty())
10976 roman = wxT("0");
10977 return roman;
10978}
42688aea 10979
44cc96a8
JS
10980/*!
10981 * wxRichTextFileHandler
10982 * Base class for file handlers
10983 */
4d6d8bf4 10984
44cc96a8 10985IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 10986
44cc96a8
JS
10987#if wxUSE_FFILE && wxUSE_STREAMS
10988bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 10989{
44cc96a8 10990 wxFFileInputStream stream(filename);
a1b806b9 10991 if (stream.IsOk())
44cc96a8 10992 return LoadFile(buffer, stream);
5d7836c4 10993
44cc96a8
JS
10994 return false;
10995}
5d7836c4 10996
44cc96a8
JS
10997bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
10998{
10999 wxFFileOutputStream stream(filename);
a1b806b9 11000 if (stream.IsOk())
44cc96a8 11001 return SaveFile(buffer, stream);
5d7836c4 11002
44cc96a8
JS
11003 return false;
11004}
11005#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 11006
44cc96a8
JS
11007/// Can we handle this filename (if using files)? By default, checks the extension.
11008bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11009{
11010 wxString path, file, ext;
a51e601e 11011 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 11012
44cc96a8
JS
11013 return (ext.Lower() == GetExtension());
11014}
5d7836c4 11015
44cc96a8
JS
11016/*!
11017 * wxRichTextTextHandler
11018 * Plain text handler
11019 */
5d7836c4 11020
44cc96a8 11021IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 11022
44cc96a8
JS
11023#if wxUSE_STREAMS
11024bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11025{
11026 if (!stream.IsOk())
797e38dd
JS
11027 return false;
11028
44cc96a8
JS
11029 wxString str;
11030 int lastCh = 0;
5d7836c4 11031
44cc96a8
JS
11032 while (!stream.Eof())
11033 {
11034 int ch = stream.GetC();
5d7836c4 11035
44cc96a8
JS
11036 if (!stream.Eof())
11037 {
11038 if (ch == 10 && lastCh != 13)
11039 str += wxT('\n');
5d7836c4 11040
44cc96a8
JS
11041 if (ch > 0 && ch != 10)
11042 str += wxChar(ch);
5d7836c4 11043
44cc96a8
JS
11044 lastCh = ch;
11045 }
11046 }
5d7836c4 11047
44cc96a8
JS
11048 buffer->ResetAndClearCommands();
11049 buffer->Clear();
11050 buffer->AddParagraphs(str);
11051 buffer->UpdateRanges();
5d7836c4 11052
44cc96a8
JS
11053 return true;
11054}
5d7836c4 11055
44cc96a8
JS
11056bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11057{
11058 if (!stream.IsOk())
5d7836c4
JS
11059 return false;
11060
44cc96a8 11061 wxString text = buffer->GetText();
38f833b1 11062
44cc96a8
JS
11063 wxString newLine = wxRichTextLineBreakChar;
11064 text.Replace(newLine, wxT("\n"));
5d7836c4 11065
44cc96a8 11066 wxCharBuffer buf = text.ToAscii();
5d7836c4 11067
44cc96a8
JS
11068 stream.Write((const char*) buf, text.length());
11069 return true;
11070}
11071#endif // wxUSE_STREAMS
5d7836c4 11072
44cc96a8
JS
11073/*
11074 * Stores information about an image, in binary in-memory form
11075 */
59509217 11076
44cc96a8
JS
11077wxRichTextImageBlock::wxRichTextImageBlock()
11078{
11079 Init();
11080}
5d7836c4 11081
44cc96a8
JS
11082wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11083{
11084 Init();
11085 Copy(block);
11086}
ea160b2e 11087
44cc96a8
JS
11088wxRichTextImageBlock::~wxRichTextImageBlock()
11089{
5276b0a5 11090 wxDELETEA(m_data);
5d7836c4
JS
11091}
11092
44cc96a8 11093void wxRichTextImageBlock::Init()
5d7836c4
JS
11094{
11095 m_data = NULL;
11096 m_dataSize = 0;
d75a69e8 11097 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11098}
11099
11100void wxRichTextImageBlock::Clear()
11101{
5276b0a5 11102 wxDELETEA(m_data);
5d7836c4 11103 m_dataSize = 0;
d75a69e8 11104 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11105}
11106
11107
11108// Load the original image into a memory block.
11109// If the image is not a JPEG, we must convert it into a JPEG
11110// to conserve space.
11111// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11112// load the image a 2nd time.
11113
d75a69e8
FM
11114bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11115 wxImage& image, bool convertToJPEG)
5d7836c4
JS
11116{
11117 m_imageType = imageType;
11118
11119 wxString filenameToRead(filename);
7fe8059f 11120 bool removeFile = false;
5d7836c4 11121
62891c87 11122 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11123 return false; // Could not determine image type
5d7836c4
JS
11124
11125 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11126 {
a51e601e
FM
11127 wxString tempFile =
11128 wxFileName::CreateTempFileName(_("image"));
5d7836c4 11129
a51e601e 11130 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11131
11132 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11133 filenameToRead = tempFile;
7fe8059f 11134 removeFile = true;
5d7836c4
JS
11135
11136 m_imageType = wxBITMAP_TYPE_JPEG;
11137 }
11138 wxFile file;
11139 if (!file.Open(filenameToRead))
7fe8059f 11140 return false;
5d7836c4
JS
11141
11142 m_dataSize = (size_t) file.Length();
11143 file.Close();
11144
11145 if (m_data)
11146 delete[] m_data;
11147 m_data = ReadBlock(filenameToRead, m_dataSize);
11148
11149 if (removeFile)
11150 wxRemoveFile(filenameToRead);
11151
11152 return (m_data != NULL);
11153}
11154
11155// Make an image block from the wxImage in the given
11156// format.
d75a69e8 11157bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 11158{
5d7836c4
JS
11159 image.SetOption(wxT("quality"), quality);
11160
62891c87 11161 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11162 return false; // Could not determine image type
5d7836c4 11163
cdaed652
VZ
11164 return DoMakeImageBlock(image, imageType);
11165}
11166
11167// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11168bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11169{
11170 if (imageType == wxBITMAP_TYPE_INVALID)
11171 return false; // Could not determine image type
ce00f59b 11172
cdaed652
VZ
11173 return DoMakeImageBlock(image, imageType);
11174}
7fe8059f 11175
cdaed652
VZ
11176// Makes the image block
11177bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11178{
11179 wxMemoryOutputStream memStream;
11180 if (!image.SaveFile(memStream, imageType))
5d7836c4 11181 {
7fe8059f 11182 return false;
5d7836c4 11183 }
ce00f59b 11184
cdaed652
VZ
11185 unsigned char* block = new unsigned char[memStream.GetSize()];
11186 if (!block)
377c1ba4 11187 return false;
ce00f59b 11188
5d7836c4
JS
11189 if (m_data)
11190 delete[] m_data;
cdaed652 11191 m_data = block;
ce00f59b
VZ
11192
11193 m_imageType = imageType;
cdaed652 11194 m_dataSize = memStream.GetSize();
5d7836c4 11195
cdaed652 11196 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
11197
11198 return (m_data != NULL);
11199}
11200
5d7836c4
JS
11201// Write to a file
11202bool wxRichTextImageBlock::Write(const wxString& filename)
11203{
11204 return WriteBlock(filename, m_data, m_dataSize);
11205}
11206
11207void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11208{
11209 m_imageType = block.m_imageType;
5276b0a5 11210 wxDELETEA(m_data);
5d7836c4
JS
11211 m_dataSize = block.m_dataSize;
11212 if (m_dataSize == 0)
11213 return;
11214
11215 m_data = new unsigned char[m_dataSize];
11216 unsigned int i;
11217 for (i = 0; i < m_dataSize; i++)
11218 m_data[i] = block.m_data[i];
11219}
11220
11221//// Operators
11222void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11223{
11224 Copy(block);
11225}
11226
11227// Load a wxImage from the block
11228bool wxRichTextImageBlock::Load(wxImage& image)
11229{
11230 if (!m_data)
7fe8059f 11231 return false;
5d7836c4
JS
11232
11233 // Read in the image.
0ca07313 11234#if wxUSE_STREAMS
5d7836c4
JS
11235 wxMemoryInputStream mstream(m_data, m_dataSize);
11236 bool success = image.LoadFile(mstream, GetImageType());
11237#else
a51e601e
FM
11238 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11239 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11240
11241 if (!WriteBlock(tempFile, m_data, m_dataSize))
11242 {
7fe8059f 11243 return false;
5d7836c4
JS
11244 }
11245 success = image.LoadFile(tempFile, GetImageType());
11246 wxRemoveFile(tempFile);
11247#endif
11248
11249 return success;
11250}
11251
11252// Write data in hex to a stream
11253bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11254{
4dc7ae1a
JS
11255 if (m_dataSize == 0)
11256 return true;
11257
11258 int bufSize = 100000;
a3c12576
JS
11259 if (int(2*m_dataSize) < bufSize)
11260 bufSize = 2*m_dataSize;
4dc7ae1a 11261 char* buf = new char[bufSize+1];
351c0647
JS
11262
11263 int left = m_dataSize;
11264 int n, i, j;
11265 j = 0;
11266 while (left > 0)
5d7836c4 11267 {
351c0647
JS
11268 if (left*2 > bufSize)
11269 {
11270 n = bufSize; left -= (bufSize/2);
11271 }
11272 else
11273 {
11274 n = left*2; left = 0;
11275 }
7fe8059f 11276
351c0647
JS
11277 char* b = buf;
11278 for (i = 0; i < (n/2); i++)
11279 {
f728025e 11280 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
11281 b += 2; j ++;
11282 }
5d7836c4 11283
351c0647
JS
11284 buf[n] = 0;
11285 stream.Write((const char*) buf, n);
11286 }
4dc7ae1a 11287 delete[] buf;
5d7836c4
JS
11288 return true;
11289}
11290
11291// Read data in hex from a stream
d75a69e8 11292bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
11293{
11294 int dataSize = length/2;
11295
11296 if (m_data)
11297 delete[] m_data;
11298
046fce47
FM
11299 // create a null terminated temporary string:
11300 char str[3];
11301 str[2] = '\0';
11302
5d7836c4
JS
11303 m_data = new unsigned char[dataSize];
11304 int i;
11305 for (i = 0; i < dataSize; i ++)
11306 {
c9f78968
VS
11307 str[0] = (char)stream.GetC();
11308 str[1] = (char)stream.GetC();
5d7836c4 11309
a9465653 11310 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
11311 }
11312
11313 m_dataSize = dataSize;
11314 m_imageType = imageType;
11315
11316 return true;
11317}
11318
5d7836c4
JS
11319// Allocate and read from stream as a block of memory
11320unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11321{
11322 unsigned char* block = new unsigned char[size];
11323 if (!block)
11324 return NULL;
11325
11326 stream.Read(block, size);
11327
11328 return block;
11329}
11330
11331unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11332{
11333 wxFileInputStream stream(filename);
a1b806b9 11334 if (!stream.IsOk())
5d7836c4
JS
11335 return NULL;
11336
11337 return ReadBlock(stream, size);
11338}
11339
11340// Write memory block to stream
11341bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11342{
11343 stream.Write((void*) block, size);
11344 return stream.IsOk();
11345
11346}
11347
11348// Write memory block to file
11349bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11350{
11351 wxFileOutputStream outStream(filename);
a1b806b9 11352 if (!outStream.IsOk())
7fe8059f 11353 return false;
5d7836c4
JS
11354
11355 return WriteBlock(outStream, block, size);
11356}
11357
d2d0adc7
JS
11358// Gets the extension for the block's type
11359wxString wxRichTextImageBlock::GetExtension() const
11360{
11361 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11362 if (handler)
11363 return handler->GetExtension();
11364 else
11365 return wxEmptyString;
11366}
11367
0ca07313
JS
11368#if wxUSE_DATAOBJ
11369
11370/*!
11371 * The data object for a wxRichTextBuffer
11372 */
11373
11374const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11375
11376wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11377{
11378 m_richTextBuffer = richTextBuffer;
11379
11380 // this string should uniquely identify our format, but is otherwise
11381 // arbitrary
11382 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11383
11384 SetFormat(m_formatRichTextBuffer);
11385}
11386
11387wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11388{
11389 delete m_richTextBuffer;
11390}
11391
11392// after a call to this function, the richTextBuffer is owned by the caller and it
11393// is responsible for deleting it!
11394wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11395{
11396 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11397 m_richTextBuffer = NULL;
11398
11399 return richTextBuffer;
11400}
11401
11402wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11403{
11404 return m_formatRichTextBuffer;
11405}
11406
11407size_t wxRichTextBufferDataObject::GetDataSize() const
11408{
11409 if (!m_richTextBuffer)
11410 return 0;
11411
11412 wxString bufXML;
11413
11414 {
11415 wxStringOutputStream stream(& bufXML);
11416 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11417 {
11418 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11419 return 0;
11420 }
11421 }
11422
11423#if wxUSE_UNICODE
11424 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11425 return strlen(buffer) + 1;
11426#else
11427 return bufXML.Length()+1;
11428#endif
11429}
11430
11431bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11432{
11433 if (!pBuf || !m_richTextBuffer)
11434 return false;
11435
11436 wxString bufXML;
11437
11438 {
11439 wxStringOutputStream stream(& bufXML);
11440 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11441 {
11442 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11443 return 0;
11444 }
11445 }
11446
11447#if wxUSE_UNICODE
11448 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11449 size_t len = strlen(buffer);
11450 memcpy((char*) pBuf, (const char*) buffer, len);
11451 ((char*) pBuf)[len] = 0;
11452#else
11453 size_t len = bufXML.Length();
11454 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11455 ((char*) pBuf)[len] = 0;
11456#endif
11457
11458 return true;
11459}
11460
11461bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
11462{
5276b0a5 11463 wxDELETE(m_richTextBuffer);
0ca07313
JS
11464
11465 wxString bufXML((const char*) buf, wxConvUTF8);
11466
11467 m_richTextBuffer = new wxRichTextBuffer;
11468
11469 wxStringInputStream stream(bufXML);
11470 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
11471 {
11472 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11473
5276b0a5 11474 wxDELETE(m_richTextBuffer);
0ca07313
JS
11475
11476 return false;
11477 }
11478 return true;
11479}
11480
11481#endif
11482 // wxUSE_DATAOBJ
11483
44cc96a8
JS
11484
11485/*
11486 * wxRichTextFontTable
11487 * Manages quick access to a pool of fonts for rendering rich text
11488 */
11489
d65381ac 11490WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
11491
11492class wxRichTextFontTableData: public wxObjectRefData
11493{
11494public:
11495 wxRichTextFontTableData() {}
11496
24777478 11497 wxFont FindFont(const wxRichTextAttr& fontSpec);
44cc96a8
JS
11498
11499 wxRichTextFontTableHashMap m_hashMap;
11500};
11501
24777478 11502wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
11503{
11504 wxString facename(fontSpec.GetFontFaceName());
11505 wxString spec(wxString::Format(wxT("%d-%d-%d-%d-%s-%d"), fontSpec.GetFontSize(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), facename.c_str(), (int) fontSpec.GetFontEncoding()));
11506 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
11507
11508 if ( entry == m_hashMap.end() )
11509 {
11510 wxFont font(fontSpec.GetFontSize(), wxDEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
11511 m_hashMap[spec] = font;
11512 return font;
11513 }
11514 else
11515 {
11516 return entry->second;
11517 }
11518}
11519
11520IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
11521
11522wxRichTextFontTable::wxRichTextFontTable()
11523{
11524 m_refData = new wxRichTextFontTableData;
44cc96a8
JS
11525}
11526
11527wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 11528 : wxObject()
44cc96a8
JS
11529{
11530 (*this) = table;
11531}
11532
11533wxRichTextFontTable::~wxRichTextFontTable()
11534{
11535 UnRef();
11536}
11537
11538bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
11539{
11540 return (m_refData == table.m_refData);
11541}
11542
11543void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
11544{
11545 Ref(table);
11546}
11547
24777478 11548wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
11549{
11550 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
11551 if (data)
11552 return data->FindFont(fontSpec);
11553 else
11554 return wxFont();
11555}
11556
11557void wxRichTextFontTable::Clear()
11558{
11559 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
11560 if (data)
11561 data->m_hashMap.clear();
11562}
11563
24777478
JS
11564// wxTextBoxAttr
11565
24777478
JS
11566void wxTextBoxAttr::Reset()
11567{
11568 m_flags = 0;
603f702b
JS
11569 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
11570 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
11571 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
11572 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 11573 m_boxStyleName = wxEmptyString;
bec80f4f 11574
24777478
JS
11575 m_margins.Reset();
11576 m_padding.Reset();
11577 m_position.Reset();
11578
603f702b 11579 m_size.Reset();
303f0be7
JS
11580 m_minSize.Reset();
11581 m_maxSize.Reset();
24777478
JS
11582
11583 m_border.Reset();
11584 m_outline.Reset();
11585}
11586
11587// Equality test
11588bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
11589{
11590 return (
11591 m_flags == attr.m_flags &&
11592 m_floatMode == attr.m_floatMode &&
11593 m_clearMode == attr.m_clearMode &&
11594 m_collapseMode == attr.m_collapseMode &&
603f702b 11595 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 11596
24777478
JS
11597 m_margins == attr.m_margins &&
11598 m_padding == attr.m_padding &&
11599 m_position == attr.m_position &&
11600
603f702b 11601 m_size == attr.m_size &&
303f0be7
JS
11602 m_minSize == attr.m_minSize &&
11603 m_maxSize == attr.m_maxSize &&
24777478
JS
11604
11605 m_border == attr.m_border &&
2f987d83
JS
11606 m_outline == attr.m_outline &&
11607
11608 m_boxStyleName == attr.m_boxStyleName
24777478
JS
11609 );
11610}
11611
11612// Partial equality test
11613bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr) const
11614{
11615 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
11616 return false;
11617
11618 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
11619 return false;
11620
11621 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
11622 return false;
11623
603f702b
JS
11624 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
11625 return false;
11626
2f987d83
JS
11627 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
11628 return false;
11629
24777478
JS
11630 // Position
11631
11632 if (!m_position.EqPartial(attr.m_position))
11633 return false;
11634
303f0be7
JS
11635 // Size
11636
11637 if (!m_size.EqPartial(attr.m_size))
11638 return false;
11639 if (!m_minSize.EqPartial(attr.m_minSize))
11640 return false;
11641 if (!m_maxSize.EqPartial(attr.m_maxSize))
11642 return false;
11643
24777478
JS
11644 // Margins
11645
11646 if (!m_margins.EqPartial(attr.m_margins))
11647 return false;
11648
11649 // Padding
11650
11651 if (!m_padding.EqPartial(attr.m_padding))
11652 return false;
11653
11654 // Border
11655
11656 if (!GetBorder().EqPartial(attr.GetBorder()))
11657 return false;
11658
11659 // Outline
11660
11661 if (!GetOutline().EqPartial(attr.GetOutline()))
11662 return false;
11663
11664 return true;
11665}
11666
11667// Merges the given attributes. If compareWith
11668// is non-NULL, then it will be used to mask out those attributes that are the same in style
11669// and compareWith, for situations where we don't want to explicitly set inherited attributes.
11670bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
11671{
11672 if (attr.HasFloatMode())
11673 {
11674 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
11675 SetFloatMode(attr.GetFloatMode());
11676 }
11677
11678 if (attr.HasClearMode())
11679 {
11680 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
11681 SetClearMode(attr.GetClearMode());
11682 }
11683
11684 if (attr.HasCollapseBorders())
11685 {
11686 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
11687 SetCollapseBorders(attr.GetCollapseBorders());
11688 }
11689
11690 if (attr.HasVerticalAlignment())
11691 {
11692 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
11693 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 11694 }
bec80f4f 11695
2f987d83
JS
11696 if (attr.HasBoxStyleName())
11697 {
11698 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
11699 SetBoxStyleName(attr.GetBoxStyleName());
11700 }
11701
bec80f4f
JS
11702 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
11703 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
11704 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 11705
603f702b 11706 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
11707 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
11708 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 11709
bec80f4f
JS
11710 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
11711 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
11712
11713 return true;
11714}
11715
11716// Remove specified attributes from this object
11717bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
11718{
11719 if (attr.HasFloatMode())
11720 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11721
11722 if (attr.HasClearMode())
11723 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11724
11725 if (attr.HasCollapseBorders())
11726 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11727
603f702b
JS
11728 if (attr.HasVerticalAlignment())
11729 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11730
2f987d83
JS
11731 if (attr.HasBoxStyleName())
11732 {
11733 SetBoxStyleName(wxEmptyString);
11734 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11735 }
11736
24777478
JS
11737 m_margins.RemoveStyle(attr.m_margins);
11738 m_padding.RemoveStyle(attr.m_padding);
11739 m_position.RemoveStyle(attr.m_position);
11740
603f702b 11741 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
11742 m_minSize.RemoveStyle(attr.m_minSize);
11743 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
11744
11745 m_border.RemoveStyle(attr.m_border);
11746 m_outline.RemoveStyle(attr.m_outline);
11747
11748 return true;
11749}
11750
11751// Collects the attributes that are common to a range of content, building up a note of
11752// which attributes are absent in some objects and which clash in some objects.
11753void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
11754{
11755 if (attr.HasFloatMode())
11756 {
11757 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
11758 {
11759 if (HasFloatMode())
11760 {
11761 if (GetFloatMode() != attr.GetFloatMode())
11762 {
11763 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
11764 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11765 }
11766 }
11767 else
11768 SetFloatMode(attr.GetFloatMode());
11769 }
11770 }
11771 else
11772 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 11773
24777478
JS
11774 if (attr.HasClearMode())
11775 {
11776 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
11777 {
11778 if (HasClearMode())
11779 {
11780 if (GetClearMode() != attr.GetClearMode())
11781 {
11782 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11783 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11784 }
11785 }
11786 else
11787 SetClearMode(attr.GetClearMode());
11788 }
11789 }
11790 else
11791 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11792
11793 if (attr.HasCollapseBorders())
11794 {
11795 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
11796 {
11797 if (HasCollapseBorders())
11798 {
11799 if (GetCollapseBorders() != attr.GetCollapseBorders())
11800 {
11801 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11802 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11803 }
11804 }
11805 else
11806 SetCollapseBorders(attr.GetCollapseBorders());
11807 }
11808 }
11809 else
11810 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 11811
603f702b
JS
11812 if (attr.HasVerticalAlignment())
11813 {
11814 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
11815 {
11816 if (HasVerticalAlignment())
11817 {
11818 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
11819 {
11820 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11821 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11822 }
11823 }
11824 else
11825 SetVerticalAlignment(attr.GetVerticalAlignment());
11826 }
11827 }
11828 else
11829 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11830
2f987d83
JS
11831 if (attr.HasBoxStyleName())
11832 {
11833 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
11834 {
11835 if (HasBoxStyleName())
11836 {
11837 if (GetBoxStyleName() != attr.GetBoxStyleName())
11838 {
11839 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11840 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11841 }
11842 }
11843 else
11844 SetBoxStyleName(attr.GetBoxStyleName());
11845 }
11846 }
11847 else
11848 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11849
24777478
JS
11850 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
11851 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
11852 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
11853
603f702b 11854 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
11855 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
11856 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
11857
11858 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
11859 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
11860}
11861
eb3d8a33
JS
11862bool wxTextBoxAttr::IsDefault() const
11863{
11864 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 11865 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
11866 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
11867}
11868
24777478
JS
11869// wxRichTextAttr
11870
11871void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
11872{
bec80f4f
JS
11873 wxTextAttr::Copy(attr);
11874
24777478
JS
11875 m_textBoxAttr = attr.m_textBoxAttr;
11876}
11877
11878bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
11879{
11880 if (!(wxTextAttr::operator==(attr)))
11881 return false;
bec80f4f 11882
24777478
JS
11883 return (m_textBoxAttr == attr.m_textBoxAttr);
11884}
11885
11886// Partial equality test taking comparison object into account
11887bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const
11888{
11889 if (!(wxTextAttr::EqPartial(attr)))
11890 return false;
bec80f4f 11891
24777478
JS
11892 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr);
11893}
11894
11895// Merges the given attributes. If compareWith
11896// is non-NULL, then it will be used to mask out those attributes that are the same in style
11897// and compareWith, for situations where we don't want to explicitly set inherited attributes.
11898bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
11899{
11900 wxTextAttr::Apply(style, compareWith);
11901
11902 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
11903}
11904
11905// Remove specified attributes from this object
11906bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
11907{
11908 wxTextAttr::RemoveStyle(*this, attr);
11909
11910 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
11911}
11912
11913// Collects the attributes that are common to a range of content, building up a note of
11914// which attributes are absent in some objects and which clash in some objects.
11915void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
11916{
11917 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 11918
24777478
JS
11919 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
11920}
11921
11922// Partial equality test
bec80f4f 11923bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border) const
24777478
JS
11924{
11925 if (border.HasStyle() && !HasStyle() && (border.GetStyle() != GetStyle()))
11926 return false;
11927
11928 if (border.HasColour() && !HasColour() && (border.GetColourLong() != GetColourLong()))
11929 return false;
11930
11931 if (border.HasWidth() && !HasWidth() && !(border.GetWidth() == GetWidth()))
11932 return false;
11933
11934 return true;
11935}
11936
11937// Apply border to 'this', but not if the same as compareWith
bec80f4f 11938bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
11939{
11940 if (border.HasStyle())
11941 {
11942 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
11943 SetStyle(border.GetStyle());
11944 }
11945 if (border.HasColour())
11946 {
11947 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
11948 SetColour(border.GetColourLong());
11949 }
11950 if (border.HasWidth())
11951 {
11952 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
11953 SetWidth(border.GetWidth());
11954 }
11955
11956 return true;
11957}
11958
11959// Remove specified attributes from this object
bec80f4f 11960bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
11961{
11962 if (attr.HasStyle() && HasStyle())
11963 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
11964 if (attr.HasColour() && HasColour())
11965 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
11966 if (attr.HasWidth() && HasWidth())
11967 m_borderWidth.Reset();
11968
11969 return true;
11970}
11971
11972// Collects the attributes that are common to a range of content, building up a note of
11973// which attributes are absent in some objects and which clash in some objects.
bec80f4f 11974void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
11975{
11976 if (attr.HasStyle())
11977 {
11978 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
11979 {
11980 if (HasStyle())
11981 {
11982 if (GetStyle() != attr.GetStyle())
11983 {
11984 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11985 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11986 }
11987 }
11988 else
11989 SetStyle(attr.GetStyle());
11990 }
11991 }
11992 else
11993 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11994
11995 if (attr.HasColour())
11996 {
11997 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
11998 {
11999 if (HasColour())
12000 {
12001 if (GetColour() != attr.GetColour())
12002 {
12003 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12004 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12005 }
12006 }
12007 else
12008 SetColour(attr.GetColourLong());
12009 }
12010 }
12011 else
12012 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 12013
24777478
JS
12014 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12015}
12016
12017// Partial equality test
bec80f4f 12018bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders) const
24777478
JS
12019{
12020 return m_left.EqPartial(borders.m_left) && m_right.EqPartial(borders.m_right) &&
12021 m_top.EqPartial(borders.m_top) && m_bottom.EqPartial(borders.m_bottom);
12022}
12023
12024// Apply border to 'this', but not if the same as compareWith
bec80f4f 12025bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 12026{
bec80f4f
JS
12027 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12028 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12029 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12030 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
12031 return true;
12032}
12033
12034// Remove specified attributes from this object
bec80f4f 12035bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
12036{
12037 m_left.RemoveStyle(attr.m_left);
12038 m_right.RemoveStyle(attr.m_right);
12039 m_top.RemoveStyle(attr.m_top);
12040 m_bottom.RemoveStyle(attr.m_bottom);
12041 return true;
12042}
12043
12044// Collects the attributes that are common to a range of content, building up a note of
12045// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12046void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
12047{
12048 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12049 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12050 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12051 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12052}
12053
12054// Set style of all borders
bec80f4f 12055void wxTextAttrBorders::SetStyle(int style)
24777478
JS
12056{
12057 m_left.SetStyle(style);
12058 m_right.SetStyle(style);
12059 m_top.SetStyle(style);
12060 m_bottom.SetStyle(style);
12061}
12062
12063// Set colour of all borders
bec80f4f 12064void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
12065{
12066 m_left.SetColour(colour);
12067 m_right.SetColour(colour);
12068 m_top.SetColour(colour);
12069 m_bottom.SetColour(colour);
12070}
12071
bec80f4f 12072void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
12073{
12074 m_left.SetColour(colour);
12075 m_right.SetColour(colour);
12076 m_top.SetColour(colour);
12077 m_bottom.SetColour(colour);
12078}
12079
12080// Set width of all borders
bec80f4f 12081void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
12082{
12083 m_left.SetWidth(width);
12084 m_right.SetWidth(width);
12085 m_top.SetWidth(width);
12086 m_bottom.SetWidth(width);
12087}
12088
12089// Partial equality test
12090bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim) const
12091{
603f702b 12092 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
12093 return false;
12094 else
12095 return true;
12096}
12097
12098bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12099{
603f702b 12100 if (dim.IsValid())
24777478
JS
12101 {
12102 if (!(compareWith && dim == (*compareWith)))
12103 (*this) = dim;
12104 }
12105
12106 return true;
12107}
12108
12109// Collects the attributes that are common to a range of content, building up a note of
12110// which attributes are absent in some objects and which clash in some objects.
12111void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12112{
603f702b 12113 if (attr.IsValid())
24777478 12114 {
603f702b 12115 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 12116 {
603f702b 12117 if (IsValid())
24777478
JS
12118 {
12119 if (!((*this) == attr))
12120 {
603f702b
JS
12121 clashingAttr.SetValid(true);
12122 SetValid(false);
24777478
JS
12123 }
12124 }
12125 else
12126 (*this) = attr;
12127 }
12128 }
12129 else
603f702b 12130 absentAttr.SetValid(true);
24777478
JS
12131}
12132
8995db52
JS
12133wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12134{
12135 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12136}
12137
12138wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12139{
12140 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12141}
12142
bec80f4f
JS
12143int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12144{
12145 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12146}
12147
12148int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12149{
12150 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12151}
12152
12153int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12154{
12155 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12156 return ConvertTenthsMMToPixels(dim.GetValue());
12157 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12158 return dim.GetValue();
12159 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12160 {
12161 wxASSERT(m_parentSize != wxDefaultSize);
12162 if (direction == wxHORIZONTAL)
12163 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12164 else
12165 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12166 }
12167 else
12168 {
12169 wxASSERT(false);
12170 return 0;
12171 }
12172}
12173
12174int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12175{
12176 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12177 return dim.GetValue();
12178 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12179 return ConvertPixelsToTenthsMM(dim.GetValue());
12180 else
12181 {
12182 wxASSERT(false);
12183 return 0;
12184 }
12185}
12186
24777478 12187// Partial equality test
bec80f4f 12188bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims) const
24777478
JS
12189{
12190 if (!m_left.EqPartial(dims.m_left))
12191 return false;
12192
12193 if (!m_right.EqPartial(dims.m_right))
12194 return false;
12195
12196 if (!m_top.EqPartial(dims.m_top))
12197 return false;
12198
12199 if (!m_bottom.EqPartial(dims.m_bottom))
12200 return false;
12201
12202 return true;
12203}
12204
12205// Apply border to 'this', but not if the same as compareWith
bec80f4f 12206bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
12207{
12208 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12209 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12210 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12211 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12212
12213 return true;
12214}
12215
12216// Remove specified attributes from this object
bec80f4f 12217bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 12218{
603f702b 12219 if (attr.m_left.IsValid())
24777478 12220 m_left.Reset();
603f702b 12221 if (attr.m_right.IsValid())
24777478 12222 m_right.Reset();
603f702b 12223 if (attr.m_top.IsValid())
24777478 12224 m_top.Reset();
603f702b 12225 if (attr.m_bottom.IsValid())
24777478
JS
12226 m_bottom.Reset();
12227
12228 return true;
12229}
12230
12231// Collects the attributes that are common to a range of content, building up a note of
12232// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12233void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
12234{
12235 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12236 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12237 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12238 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12239}
12240
603f702b
JS
12241// Partial equality test
12242bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size) const
12243{
12244 if (!m_width.EqPartial(size.m_width))
12245 return false;
12246
12247 if (!m_height.EqPartial(size.m_height))
12248 return false;
12249
12250 return true;
12251}
12252
12253// Apply border to 'this', but not if the same as compareWith
12254bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12255{
12256 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12257 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12258
12259 return true;
12260}
12261
12262// Remove specified attributes from this object
12263bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12264{
12265 if (attr.m_width.IsValid())
12266 m_width.Reset();
12267 if (attr.m_height.IsValid())
12268 m_height.Reset();
12269
12270 return true;
12271}
12272
12273// Collects the attributes that are common to a range of content, building up a note of
12274// which attributes are absent in some objects and which clash in some objects.
12275void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12276{
12277 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12278 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12279}
12280
24777478
JS
12281// Collects the attributes that are common to a range of content, building up a note of
12282// which attributes are absent in some objects and which clash in some objects.
12283void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12284{
12285 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12286 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12287
12288 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12289
12290 if (attr.HasFont())
12291 {
12292 if (attr.HasFontSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_SIZE))
12293 {
12294 if (currentStyle.HasFontSize())
12295 {
12296 if (currentStyle.GetFontSize() != attr.GetFontSize())
12297 {
12298 // Clash of attr - mark as such
12299 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12300 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12301 }
12302 }
12303 else
12304 currentStyle.SetFontSize(attr.GetFontSize());
12305 }
12306
12307 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12308 {
12309 if (currentStyle.HasFontItalic())
12310 {
12311 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
12312 {
12313 // Clash of attr - mark as such
12314 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12315 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12316 }
12317 }
12318 else
12319 currentStyle.SetFontStyle(attr.GetFontStyle());
12320 }
12321
12322 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12323 {
12324 if (currentStyle.HasFontFamily())
12325 {
12326 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
12327 {
12328 // Clash of attr - mark as such
12329 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12330 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12331 }
12332 }
12333 else
12334 currentStyle.SetFontFamily(attr.GetFontFamily());
12335 }
12336
12337 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12338 {
12339 if (currentStyle.HasFontWeight())
12340 {
12341 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
12342 {
12343 // Clash of attr - mark as such
12344 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12345 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12346 }
12347 }
12348 else
12349 currentStyle.SetFontWeight(attr.GetFontWeight());
12350 }
12351
12352 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12353 {
12354 if (currentStyle.HasFontFaceName())
12355 {
12356 wxString faceName1(currentStyle.GetFontFaceName());
12357 wxString faceName2(attr.GetFontFaceName());
12358
12359 if (faceName1 != faceName2)
12360 {
12361 // Clash of attr - mark as such
12362 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
12363 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
12364 }
12365 }
12366 else
12367 currentStyle.SetFontFaceName(attr.GetFontFaceName());
12368 }
12369
12370 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
12371 {
12372 if (currentStyle.HasFontUnderlined())
12373 {
12374 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
12375 {
12376 // Clash of attr - mark as such
12377 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12378 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12379 }
12380 }
12381 else
12382 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
12383 }
12384 }
12385
12386 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
12387 {
12388 if (currentStyle.HasTextColour())
12389 {
12390 if (currentStyle.GetTextColour() != attr.GetTextColour())
12391 {
12392 // Clash of attr - mark as such
12393 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
12394 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
12395 }
12396 }
12397 else
12398 currentStyle.SetTextColour(attr.GetTextColour());
12399 }
12400
12401 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
12402 {
12403 if (currentStyle.HasBackgroundColour())
12404 {
12405 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
12406 {
12407 // Clash of attr - mark as such
12408 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
12409 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
12410 }
12411 }
12412 else
12413 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
12414 }
12415
12416 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
12417 {
12418 if (currentStyle.HasAlignment())
12419 {
12420 if (currentStyle.GetAlignment() != attr.GetAlignment())
12421 {
12422 // Clash of attr - mark as such
12423 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
12424 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
12425 }
12426 }
12427 else
12428 currentStyle.SetAlignment(attr.GetAlignment());
12429 }
12430
12431 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
12432 {
12433 if (currentStyle.HasTabs())
12434 {
12435 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
12436 {
12437 // Clash of attr - mark as such
12438 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
12439 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
12440 }
12441 }
12442 else
12443 currentStyle.SetTabs(attr.GetTabs());
12444 }
12445
12446 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
12447 {
12448 if (currentStyle.HasLeftIndent())
12449 {
12450 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
12451 {
12452 // Clash of attr - mark as such
12453 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
12454 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
12455 }
12456 }
12457 else
12458 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
12459 }
12460
12461 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
12462 {
12463 if (currentStyle.HasRightIndent())
12464 {
12465 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
12466 {
12467 // Clash of attr - mark as such
12468 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
12469 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
12470 }
12471 }
12472 else
12473 currentStyle.SetRightIndent(attr.GetRightIndent());
12474 }
12475
12476 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
12477 {
12478 if (currentStyle.HasParagraphSpacingAfter())
12479 {
12480 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
12481 {
12482 // Clash of attr - mark as such
12483 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
12484 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
12485 }
12486 }
12487 else
12488 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
12489 }
12490
12491 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
12492 {
12493 if (currentStyle.HasParagraphSpacingBefore())
12494 {
12495 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
12496 {
12497 // Clash of attr - mark as such
12498 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
12499 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
12500 }
12501 }
12502 else
12503 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
12504 }
12505
12506 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
12507 {
12508 if (currentStyle.HasLineSpacing())
12509 {
12510 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
12511 {
12512 // Clash of attr - mark as such
12513 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
12514 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
12515 }
12516 }
12517 else
12518 currentStyle.SetLineSpacing(attr.GetLineSpacing());
12519 }
12520
12521 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
12522 {
12523 if (currentStyle.HasCharacterStyleName())
12524 {
12525 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
12526 {
12527 // Clash of attr - mark as such
12528 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
12529 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
12530 }
12531 }
12532 else
12533 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
12534 }
12535
12536 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
12537 {
12538 if (currentStyle.HasParagraphStyleName())
12539 {
12540 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
12541 {
12542 // Clash of attr - mark as such
12543 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
12544 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
12545 }
12546 }
12547 else
12548 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
12549 }
12550
12551 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
12552 {
12553 if (currentStyle.HasListStyleName())
12554 {
12555 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
12556 {
12557 // Clash of attr - mark as such
12558 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
12559 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
12560 }
12561 }
12562 else
12563 currentStyle.SetListStyleName(attr.GetListStyleName());
12564 }
12565
12566 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
12567 {
12568 if (currentStyle.HasBulletStyle())
12569 {
12570 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
12571 {
12572 // Clash of attr - mark as such
12573 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
12574 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
12575 }
12576 }
12577 else
12578 currentStyle.SetBulletStyle(attr.GetBulletStyle());
12579 }
12580
12581 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
12582 {
12583 if (currentStyle.HasBulletNumber())
12584 {
12585 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
12586 {
12587 // Clash of attr - mark as such
12588 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
12589 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
12590 }
12591 }
12592 else
12593 currentStyle.SetBulletNumber(attr.GetBulletNumber());
12594 }
12595
12596 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
12597 {
12598 if (currentStyle.HasBulletText())
12599 {
12600 if (currentStyle.GetBulletText() != attr.GetBulletText())
12601 {
12602 // Clash of attr - mark as such
12603 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
12604 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
12605 }
12606 }
12607 else
12608 {
12609 currentStyle.SetBulletText(attr.GetBulletText());
12610 currentStyle.SetBulletFont(attr.GetBulletFont());
12611 }
12612 }
12613
12614 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
12615 {
12616 if (currentStyle.HasBulletName())
12617 {
12618 if (currentStyle.GetBulletName() != attr.GetBulletName())
12619 {
12620 // Clash of attr - mark as such
12621 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
12622 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
12623 }
12624 }
12625 else
12626 {
12627 currentStyle.SetBulletName(attr.GetBulletName());
12628 }
12629 }
12630
12631 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
12632 {
12633 if (currentStyle.HasURL())
12634 {
12635 if (currentStyle.GetURL() != attr.GetURL())
12636 {
12637 // Clash of attr - mark as such
12638 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
12639 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
12640 }
12641 }
12642 else
12643 {
12644 currentStyle.SetURL(attr.GetURL());
12645 }
12646 }
12647
12648 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
12649 {
12650 if (currentStyle.HasTextEffects())
12651 {
12652 // We need to find the bits in the new attr that are different:
12653 // just look at those bits that are specified by the new attr.
12654
12655 // We need to remove the bits and flags that are not common between current attr
12656 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12657 // previous styles.
12658
12659 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
12660 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
12661
12662 if (currentRelevantTextEffects != newRelevantTextEffects)
12663 {
12664 // Find the text effects that were different, using XOR
12665 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
12666
12667 // Clash of attr - mark as such
12668 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
12669 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
12670 }
12671 }
12672 else
12673 {
12674 currentStyle.SetTextEffects(attr.GetTextEffects());
12675 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
12676 }
12677
12678 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12679 // that we've looked at so far
12680 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
12681 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
12682
12683 if (currentStyle.GetTextEffectFlags() == 0)
12684 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
12685 }
12686
12687 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
12688 {
12689 if (currentStyle.HasOutlineLevel())
12690 {
12691 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
12692 {
12693 // Clash of attr - mark as such
12694 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12695 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12696 }
12697 }
12698 else
12699 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
12700 }
12701}
12702
bec80f4f
JS
12703WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
12704
12705IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
12706
12707bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
12708{
12709 if (m_properties.GetCount() != props.GetCount())
12710 return false;
12711
12712 size_t i;
12713 for (i = 0; i < m_properties.GetCount(); i++)
12714 {
12715 const wxVariant& var1 = m_properties[i];
12716 int idx = props.Find(var1.GetName());
12717 if (idx == -1)
12718 return false;
12719 const wxVariant& var2 = props.m_properties[idx];
12720 if (!(var1 == var2))
12721 return false;
12722 }
12723
12724 return true;
12725}
12726
12727wxArrayString wxRichTextProperties::GetPropertyNames() const
12728{
12729 wxArrayString arr;
12730 size_t i;
12731 for (i = 0; i < m_properties.GetCount(); i++)
12732 {
12733 arr.Add(m_properties[i].GetName());
12734 }
12735 return arr;
12736}
12737
12738int wxRichTextProperties::Find(const wxString& name) const
12739{
12740 size_t i;
12741 for (i = 0; i < m_properties.GetCount(); i++)
12742 {
12743 if (m_properties[i].GetName() == name)
12744 return (int) i;
12745 }
12746 return -1;
12747}
12748
590a0f8b
JS
12749bool wxRichTextProperties::Remove(const wxString& name)
12750{
12751 int idx = Find(name);
12752 if (idx != -1)
12753 {
12754 m_properties.RemoveAt(idx);
12755 return true;
12756 }
12757 else
12758 return false;
12759}
12760
bec80f4f
JS
12761wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
12762{
12763 int idx = Find(name);
12764 if (idx == wxNOT_FOUND)
12765 SetProperty(name, wxString());
12766 idx = Find(name);
12767 if (idx != wxNOT_FOUND)
12768 {
12769 return & (*this)[idx];
12770 }
12771 else
12772 return NULL;
12773}
12774
12775const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
12776{
12777 static const wxVariant nullVariant;
12778 int idx = Find(name);
12779 if (idx != -1)
12780 return m_properties[idx];
12781 else
12782 return nullVariant;
12783}
12784
12785wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
12786{
12787 return GetProperty(name).GetString();
12788}
12789
12790long wxRichTextProperties::GetPropertyLong(const wxString& name) const
12791{
12792 return GetProperty(name).GetLong();
12793}
12794
12795bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
12796{
12797 return GetProperty(name).GetBool();
12798}
12799
12800double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
12801{
12802 return GetProperty(name).GetDouble();
12803}
12804
12805void wxRichTextProperties::SetProperty(const wxVariant& variant)
12806{
12807 wxASSERT(!variant.GetName().IsEmpty());
12808
12809 int idx = Find(variant.GetName());
12810
12811 if (idx == -1)
12812 m_properties.Add(variant);
12813 else
12814 m_properties[idx] = variant;
12815}
12816
12817void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
12818{
12819 int idx = Find(name);
12820 wxVariant var(variant);
12821 var.SetName(name);
12822
12823 if (idx == -1)
12824 m_properties.Add(var);
12825 else
12826 m_properties[idx] = var;
12827}
12828
12829void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
12830{
12831 SetProperty(name, wxVariant(value, name));
12832}
12833
12834void wxRichTextProperties::SetProperty(const wxString& name, long value)
12835{
12836 SetProperty(name, wxVariant(value, name));
12837}
12838
12839void wxRichTextProperties::SetProperty(const wxString& name, double value)
12840{
12841 SetProperty(name, wxVariant(value, name));
12842}
12843
12844void wxRichTextProperties::SetProperty(const wxString& name, bool value)
12845{
12846 SetProperty(name, wxVariant(value, name));
12847}
24777478 12848
590a0f8b
JS
12849void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
12850{
12851 size_t i;
12852 for (i = 0; i < properties.GetCount(); i++)
12853 {
12854 wxString name = properties.GetProperties()[i].GetName();
12855 if (HasProperty(name))
12856 Remove(name);
12857 }
12858}
12859
12860void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
12861{
12862 size_t i;
12863 for (i = 0; i < properties.GetCount(); i++)
12864 {
12865 SetProperty(properties.GetProperties()[i]);
12866 }
12867}
12868
603f702b
JS
12869wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
12870{
12871 if (m_address.GetCount() == 0)
12872 return topLevelContainer;
12873
12874 wxRichTextCompositeObject* p = topLevelContainer;
12875 size_t i = 0;
12876 while (p && i < m_address.GetCount())
12877 {
12878 int pos = m_address[i];
12879 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
12880 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
12881 return NULL;
12882
12883 wxRichTextObject* p1 = p->GetChild(pos);
12884 if (i == (m_address.GetCount()-1))
12885 return p1;
12886
12887 p = wxDynamicCast(p1, wxRichTextCompositeObject);
12888 i ++;
12889 }
12890 return NULL;
12891}
12892
12893bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
12894{
12895 m_address.Clear();
12896
12897 if (topLevelContainer == obj)
12898 return true;
12899
12900 wxRichTextObject* o = obj;
12901 while (o)
12902 {
12903 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
12904 if (!p)
12905 return false;
12906
12907 int pos = p->GetChildren().IndexOf(o);
12908 if (pos == -1)
12909 return false;
12910
12911 m_address.Insert(pos, 0);
12912
12913 if (p == topLevelContainer)
12914 return true;
12915
12916 o = p;
12917 }
12918 return false;
12919}
12920
12921// Equality test
12922bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
12923{
12924 if (m_container != sel.m_container)
12925 return false;
12926 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
12927 return false;
12928 size_t i;
12929 for (i = 0; i < m_ranges.GetCount(); i++)
12930 if (!(m_ranges[i] == sel.m_ranges[i]))
12931 return false;
12932 return true;
12933}
12934
12935// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12936// or none at the level of the object's container.
12937wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
12938{
12939 if (IsValid())
12940 {
12941 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
12942
12943 if (container == m_container)
12944 return m_ranges;
12945
12946 container = obj->GetContainer();
12947 while (container)
12948 {
12949 if (container->GetParent())
12950 {
12951 // If we found that our object's container is within the range of
12952 // a selection higher up, then assume the whole original object
12953 // is also selected.
12954 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
12955 if (parentContainer == m_container)
12956 {
12957 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
12958 {
12959 wxRichTextRangeArray ranges;
12960 ranges.Add(obj->GetRange());
12961 return ranges;
12962 }
12963 }
12964
12965 container = parentContainer;
12966 }
12967 else
12968 {
12969 container = NULL;
12970 break;
12971 }
12972 }
12973 }
12974 return wxRichTextRangeArray();
12975}
12976
12977// Is the given position within the selection?
12978bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
12979{
12980 if (!IsValid())
12981 return false;
12982 else
12983 {
12984 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
12985 return WithinSelection(pos, selectionRanges);
12986 }
12987}
12988
12989// Is the given position within the selection range?
12990bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
12991{
12992 size_t i;
12993 for (i = 0; i < ranges.GetCount(); i++)
12994 {
12995 const wxRichTextRange& range = ranges[i];
12996 if (pos >= range.GetStart() && pos <= range.GetEnd())
12997 return true;
12998 }
12999 return false;
13000}
13001
13002// Is the given range completely within the selection range?
13003bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13004{
13005 size_t i;
13006 for (i = 0; i < ranges.GetCount(); i++)
13007 {
13008 const wxRichTextRange& eachRange = ranges[i];
13009 if (range.IsWithin(eachRange))
13010 return true;
13011 }
13012 return false;
13013}
13014
8db2e3ef
JS
13015IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13016IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13017
13018bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13019{
13020 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13021 while (node)
13022 {
13023 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13024 if (handler->HasVirtualAttributes(obj))
13025 return true;
13026
13027 node = node->GetNext();
13028 }
13029 return false;
13030}
13031
13032wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13033{
13034 wxRichTextAttr attr;
13035 // We apply all handlers, so we can may combine several different attributes
13036 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13037 while (node)
13038 {
13039 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13040 if (handler->HasVirtualAttributes(obj))
13041 {
13042 bool success = handler->GetVirtualAttributes(attr, obj);
13043 wxASSERT(success);
aa8f57f4 13044 wxUnusedVar(success);
8db2e3ef
JS
13045 }
13046
13047 node = node->GetNext();
13048 }
13049 return attr;
13050}
13051
13052bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13053{
13054 if (HasVirtualAttributes(obj))
13055 {
13056 wxRichTextAttr a(GetVirtualAttributes(obj));
13057 attr.Apply(a);
13058 return true;
13059 }
13060 else
13061 return false;
13062}
13063
13064/// Adds a handler to the end
13065void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13066{
13067 sm_drawingHandlers.Append(handler);
13068}
13069
13070/// Inserts a handler at the front
13071void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13072{
13073 sm_drawingHandlers.Insert( handler );
13074}
13075
13076/// Removes a handler
13077bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13078{
13079 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13080 if (handler)
13081 {
13082 sm_drawingHandlers.DeleteObject(handler);
13083 delete handler;
13084 return true;
13085 }
13086 else
13087 return false;
13088}
13089
13090wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13091{
13092 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13093 while (node)
13094 {
13095 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13096 if (handler->GetName().Lower() == name.Lower()) return handler;
13097
13098 node = node->GetNext();
13099 }
13100 return NULL;
13101}
13102
13103void wxRichTextBuffer::CleanUpDrawingHandlers()
13104{
13105 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13106 while (node)
13107 {
13108 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13109 wxList::compatibility_iterator next = node->GetNext();
13110 delete handler;
13111 node = next;
13112 }
13113
13114 sm_drawingHandlers.Clear();
13115}
603f702b 13116
7c9fdebe
JS
13117void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13118{
13119 sm_fieldTypes[fieldType->GetName()] = fieldType;
13120}
13121
13122bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13123{
13124 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13125 if (it == sm_fieldTypes.end())
13126 return false;
13127 else
13128 {
13129 wxRichTextFieldType* fieldType = it->second;
13130 sm_fieldTypes.erase(it);
13131 delete fieldType;
13132 return true;
13133 }
13134}
13135
13136wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13137{
13138 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13139 if (it == sm_fieldTypes.end())
13140 return NULL;
13141 else
13142 return it->second;
13143}
13144
13145void wxRichTextBuffer::CleanUpFieldTypes()
13146{
13147 wxRichTextFieldTypeHashMap::iterator it;
13148 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
13149 {
13150 wxRichTextFieldType* fieldType = it->second;
13151 delete fieldType;
13152 }
13153
13154 sm_fieldTypes.clear();
13155}
13156
5d7836c4
JS
13157#endif
13158 // wxUSE_RICHTEXT