]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
Added custom properties to style definitions and style sheets
[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"
5d7836c4
JS
45
46#include "wx/listimpl.cpp"
bec80f4f 47#include "wx/arrimpl.cpp"
5d7836c4 48
412e0d47
DS
49WX_DEFINE_LIST(wxRichTextObjectList)
50WX_DEFINE_LIST(wxRichTextLineList)
5d7836c4 51
ea160b2e
JS
52// Switch off if the platform doesn't like it for some reason
53#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
54
31778480
JS
55// Use GetPartialTextExtents for platforms that support it natively
56#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
57
ff76711f
JS
58const wxChar wxRichTextLineBreakChar = (wxChar) 29;
59
cdaed652
VZ
60// Helper classes for floating layout
61struct wxRichTextFloatRectMap
62{
63 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
64 {
65 startY = sY;
66 endY = eY;
67 width = w;
68 anchor = obj;
69 }
70
71 int startY, endY;
72 int width;
73 wxRichTextObject* anchor;
74};
75
76WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
77
78int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
79{
80 return r1->startY - r2->startY;
81}
82
83class wxRichTextFloatCollector
84{
85public:
603f702b 86 wxRichTextFloatCollector(const wxRect& availableRect);
cdaed652
VZ
87 ~wxRichTextFloatCollector();
88
89 // Collect the floating objects info in the given paragraph
90 void CollectFloat(wxRichTextParagraph* para);
91 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
92
93 // Return the last paragraph we collected
94 wxRichTextParagraph* LastParagraph();
95
96 // Given the start y position and the height of the line,
97 // find out how wide the line can be
98 wxRect GetAvailableRect(int startY, int endY);
99
100 // Given a floating box, find its fit position
101 int GetFitPosition(int direction, int start, int height) const;
102 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
103
104 // Find the last y position
105 int GetLastRectBottom();
106
107 // Draw the floats inside a rect
8db2e3ef 108 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652
VZ
109
110 // HitTest the floats
8db2e3ef 111 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
603f702b
JS
112
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
115
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
cdaed652 118
07d4142f
JS
119 // Delete a float
120 bool DeleteFloat(wxRichTextObject* obj);
121
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject* obj);
124
125 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
126
cdaed652
VZ
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
128
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
ce00f59b 130
cdaed652
VZ
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
132
8db2e3ef 133 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652 134
8db2e3ef 135 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
ce00f59b 136
cdaed652
VZ
137private:
138 wxRichTextFloatRectMapArray m_left;
139 wxRichTextFloatRectMapArray m_right;
603f702b
JS
140 //int m_width;
141 wxRect m_availableRect;
cdaed652
VZ
142 wxRichTextParagraph* m_para;
143};
144
07d4142f
JS
145// Delete a float
146bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
147{
148 size_t i;
149 for (i = 0; i < m_left.GetCount(); i++)
150 {
151 if (m_left[i]->anchor == obj)
152 {
153 m_left.RemoveAt(i);
154 return true;
155 }
156 }
157 for (i = 0; i < m_right.GetCount(); i++)
158 {
159 if (m_right[i]->anchor == obj)
160 {
161 m_right.RemoveAt(i);
162 return true;
163 }
164 }
165 return false;
166}
167
168// Do we have this float already?
169bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
170{
171 size_t i;
172 for (i = 0; i < m_left.GetCount(); i++)
173 {
174 if (m_left[i]->anchor == obj)
175 {
176 return true;
177 }
178 }
179 for (i = 0; i < m_right.GetCount(); i++)
180 {
181 if (m_right[i]->anchor == obj)
182 {
183 return true;
184 }
185 }
186 return false;
187}
188
603f702b
JS
189// Get floating objects
190bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
191{
192 size_t i;
193 for (i = 0; i < m_left.GetCount(); i++)
194 objects.Append(m_left[i]->anchor);
195 for (i = 0; i < m_right.GetCount(); i++)
196 objects.Append(m_right[i]->anchor);
197 return true;
198}
199
200
cdaed652
VZ
201/*
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
206 */
207int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
208{
209 int end = array.GetCount() - 1;
210 int start = 0;
211 int ret = 0;
212
213 wxASSERT(end >= 0);
214
215 while (true)
216 {
217 if (start > end)
218 {
219 break;
220 }
221
222 int mid = (start + end) / 2;
223 if (array[mid]->startY <= point && array[mid]->endY >= point)
224 return mid;
225 else if (array[mid]->startY > point)
226 {
227 end = mid - 1;
228 ret = mid;
229 }
230 else if (array[mid]->endY < point)
231 {
232 start = mid + 1;
233 ret = start;
234 }
235 }
236
237 return ret;
238}
239
240int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
241{
242 int ret = 0;
243 int len = array.GetCount();
244
245 wxASSERT(index >= 0 && index < len);
246
247 if (array[index]->startY < startY && array[index]->endY > startY)
248 ret = ret < array[index]->width ? array[index]->width : ret;
249 while (index < len && array[index]->startY <= endY)
250 {
251 ret = ret < array[index]->width ? array[index]->width : ret;
252 index++;
253 }
254
255 return ret;
256}
257
603f702b 258wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
cdaed652 259{
603f702b 260 m_availableRect = rect;
cdaed652
VZ
261 m_para = NULL;
262}
263
264void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
265{
266 int len = array.GetCount();
267 for (int i = 0; i < len; i++)
268 delete array[i];
269}
270
271wxRichTextFloatCollector::~wxRichTextFloatCollector()
272{
273 FreeFloatRectMapArray(m_left);
274 FreeFloatRectMapArray(m_right);
275}
276
277int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
278{
279 if (array.GetCount() == 0)
280 return start;
281
603f702b 282 int i = SearchAdjacentRect(array, start);
cdaed652 283 int last = start;
603f702b 284 while (i < (int) array.GetCount())
cdaed652
VZ
285 {
286 if (array[i]->startY - last >= height)
287 return last + 1;
288 last = array[i]->endY;
289 i++;
290 }
291
292 return last + 1;
293}
294
295int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
296{
24777478 297 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
cdaed652 298 return GetFitPosition(m_left, start, height);
24777478 299 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
cdaed652
VZ
300 return GetFitPosition(m_right, start, height);
301 else
302 {
303 wxASSERT("Never should be here");
304 return start;
305 }
306}
307
603f702b
JS
308// Adds a floating image to the float collector.
309// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
cdaed652
VZ
310void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
311{
603f702b 312 int direction = floating->GetFloatDirection();
24777478 313
603f702b
JS
314 wxPoint pos = floating->GetPosition();
315 wxSize size = floating->GetCachedSize();
316 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
317 switch (direction)
318 {
319 case wxTEXT_BOX_ATTR_FLOAT_NONE:
320 delete map;
321 break;
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
325 m_left.Add(map);
326 break;
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
328 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
329 m_right.Add(map);
330 break;
331 default:
332 delete map;
333 wxASSERT("Unrecognised float attribute.");
334 }
cdaed652 335
603f702b 336 m_para = para;
cdaed652
VZ
337}
338
339void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
340{
341 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
342 while (node)
343 {
344 wxRichTextObject* floating = node->GetData();
ce00f59b 345
cdaed652
VZ
346 if (floating->IsFloating())
347 {
bec80f4f 348 CollectFloat(para, floating);
cdaed652 349 }
ce00f59b 350
cdaed652
VZ
351 node = node->GetNext();
352 }
353
354 m_para = para;
355}
356
357wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
358{
359 return m_para;
360}
361
362wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
363{
364 int widthLeft = 0, widthRight = 0;
365 if (m_left.GetCount() != 0)
366 {
603f702b
JS
367 int i = SearchAdjacentRect(m_left, startY);
368 if (i < (int) m_left.GetCount())
cdaed652
VZ
369 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
370 }
371 if (m_right.GetCount() != 0)
372 {
603f702b
JS
373 int j = SearchAdjacentRect(m_right, startY);
374 if (j < (int) m_right.GetCount())
cdaed652
VZ
375 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
376 }
377
603f702b
JS
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
cdaed652
VZ
382}
383
384int wxRichTextFloatCollector::GetLastRectBottom()
385{
386 int ret = 0;
387 int len = m_left.GetCount();
388 if (len) {
389 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
390 }
391 len = m_right.GetCount();
392 if (len) {
393 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
394 }
395
396 return ret;
397}
398
8db2e3ef 399void 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
400{
401 int start = rect.y;
402 int end = rect.y + rect.height;
603f702b 403 int i, j;
cdaed652 404 i = SearchAdjacentRect(array, start);
603f702b 405 if (i < 0 || i >= (int) array.GetCount())
cdaed652
VZ
406 return;
407 j = SearchAdjacentRect(array, end);
603f702b 408 if (j < 0 || j >= (int) array.GetCount())
cdaed652
VZ
409 j = array.GetCount() - 1;
410 while (i <= j)
411 {
412 wxRichTextObject* obj = array[i]->anchor;
413 wxRichTextRange r = obj->GetRange();
8db2e3ef 414 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
cdaed652
VZ
415 i++;
416 }
417}
ecb5fbf1 418
8db2e3ef 419void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
420{
421 if (m_left.GetCount() > 0)
8db2e3ef 422 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
cdaed652 423 if (m_right.GetCount() > 0)
8db2e3ef 424 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
cdaed652
VZ
425}
426
8db2e3ef 427int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
cdaed652 428{
603f702b 429 int i;
cdaed652
VZ
430 if (array.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE;
432 i = SearchAdjacentRect(array, pt.y);
603f702b 433 if (i < 0 || i >= (int) array.GetCount())
cdaed652 434 return wxRICHTEXT_HITTEST_NONE;
603f702b
JS
435 if (!array[i]->anchor->IsShown())
436 return wxRICHTEXT_HITTEST_NONE;
437
cdaed652
VZ
438 wxPoint point = array[i]->anchor->GetPosition();
439 wxSize size = array[i]->anchor->GetCachedSize();
440 if (point.x <= pt.x && point.x + size.x >= pt.x
441 && point.y <= pt.y && point.y + size.y >= pt.y)
442 {
443 textPosition = array[i]->anchor->GetRange().GetStart();
603f702b 444 * obj = array[i]->anchor;
cdaed652
VZ
445 if (pt.x > (pt.x + pt.x + size.x) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE;
447 else
448 return wxRICHTEXT_HITTEST_AFTER;
449 }
ce00f59b 450
cdaed652
VZ
451 return wxRICHTEXT_HITTEST_NONE;
452}
453
8db2e3ef 454int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
cdaed652 455{
8db2e3ef 456 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
457 if (ret == wxRICHTEXT_HITTEST_NONE)
458 {
8db2e3ef 459 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
460 }
461 return ret;
462}
463
ce00f59b 464// Helpers for efficiency
ecb5fbf1
JS
465inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
466{
cdaed652 467 // JACS: did I do this some time ago when testing? Should we re-enable it?
5cb0b827 468#if 0
ecb5fbf1
JS
469 const wxFont& font1 = dc.GetFont();
470 if (font1.IsOk() && font.IsOk())
471 {
472 if (font1.GetPointSize() == font.GetPointSize() &&
473 font1.GetFamily() == font.GetFamily() &&
474 font1.GetStyle() == font.GetStyle() &&
475 font1.GetWeight() == font.GetWeight() &&
3c8766f7 476 font1.GetUnderlined() == font.GetUnderlined() &&
9c4cb611 477 font1.GetFamily() == font.GetFamily() &&
ecb5fbf1
JS
478 font1.GetFaceName() == font.GetFaceName())
479 return;
480 }
5cb0b827 481#endif
ecb5fbf1
JS
482 dc.SetFont(font);
483}
484
485inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
486{
487 const wxPen& pen1 = dc.GetPen();
488 if (pen1.IsOk() && pen.IsOk())
489 {
490 if (pen1.GetWidth() == pen.GetWidth() &&
491 pen1.GetStyle() == pen.GetStyle() &&
492 pen1.GetColour() == pen.GetColour())
493 return;
494 }
495 dc.SetPen(pen);
496}
497
498inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
499{
500 const wxBrush& brush1 = dc.GetBrush();
501 if (brush1.IsOk() && brush.IsOk())
502 {
503 if (brush1.GetStyle() == brush.GetStyle() &&
504 brush1.GetColour() == brush.GetColour())
505 return;
506 }
507 dc.SetBrush(brush);
508}
509
5d7836c4
JS
510/*!
511 * wxRichTextObject
512 * This is the base for drawable objects.
513 */
514
515IMPLEMENT_CLASS(wxRichTextObject, wxObject)
516
517wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
518{
5d7836c4
JS
519 m_refCount = 1;
520 m_parent = parent;
5d7836c4 521 m_descent = 0;
603f702b 522 m_show = true;
5d7836c4
JS
523}
524
525wxRichTextObject::~wxRichTextObject()
526{
527}
528
529void wxRichTextObject::Dereference()
530{
531 m_refCount --;
532 if (m_refCount <= 0)
533 delete this;
534}
535
536/// Copy
537void wxRichTextObject::Copy(const wxRichTextObject& obj)
538{
539 m_size = obj.m_size;
603f702b
JS
540 m_maxSize = obj.m_maxSize;
541 m_minSize = obj.m_minSize;
5d7836c4 542 m_pos = obj.m_pos;
5d7836c4 543 m_range = obj.m_range;
603f702b 544 m_ownRange = obj.m_ownRange;
5d7836c4 545 m_attributes = obj.m_attributes;
bec80f4f 546 m_properties = obj.m_properties;
5d7836c4 547 m_descent = obj.m_descent;
603f702b
JS
548 m_show = obj.m_show;
549}
550
551// Get/set the top-level container of this object.
552wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
553{
554 const wxRichTextObject* p = this;
555 while (p)
556 {
557 if (p->IsTopLevel())
558 {
559 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
560 }
561 p = p->GetParent();
562 }
563 return NULL;
5d7836c4
JS
564}
565
566void wxRichTextObject::SetMargins(int margin)
567{
603f702b 568 SetMargins(margin, margin, margin, margin);
5d7836c4
JS
569}
570
571void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
572{
603f702b
JS
573 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
574 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
575 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
576 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
577}
578
579int wxRichTextObject::GetLeftMargin() const
580{
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
582}
583
584int wxRichTextObject::GetRightMargin() const
585{
586 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
587}
588
589int wxRichTextObject::GetTopMargin() const
590{
591 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
592}
593
594int wxRichTextObject::GetBottomMargin() const
595{
596 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
597}
598
599// Calculate the available content space in the given rectangle, given the
600// margins, border and padding specified in the object's attributes.
8db2e3ef 601wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
603f702b
JS
602{
603 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
604 marginRect = outerRect;
8db2e3ef
JS
605 wxRichTextAttr attr(GetAttributes());
606 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
607 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
608 return contentRect;
609}
610
611// Invalidate the buffer. With no argument, invalidates whole buffer.
612void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
613{
614 if (invalidRange != wxRICHTEXT_NONE)
615 {
616 SetCachedSize(wxDefaultSize);
617 SetMaxSize(wxDefaultSize);
618 SetMinSize(wxDefaultSize);
619 }
5d7836c4
JS
620}
621
44219ff0 622// Convert units in tenths of a millimetre to device units
cdaed652 623int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
5d7836c4 624{
44219ff0 625 // Unscale
bec80f4f
JS
626 double scale = 1.0;
627 if (GetBuffer())
628 scale = GetBuffer()->GetScale();
629 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
630
44219ff0
JS
631 return p;
632}
633
634// Convert units in tenths of a millimetre to device units
bec80f4f 635int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
44219ff0 636{
5d7836c4
JS
637 // There are ppi pixels in 254.1 "1/10 mm"
638
639 double pixels = ((double) units * (double)ppi) / 254.1;
bec80f4f
JS
640 if (scale != 1.0)
641 pixels /= scale;
5d7836c4 642
603f702b
JS
643 // If the result is very small, make it at least one pixel in size.
644 if (pixels == 0 && units > 0)
645 pixels = 1;
646
5d7836c4
JS
647 return (int) pixels;
648}
649
24777478
JS
650// Convert units in pixels to tenths of a millimetre
651int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
652{
653 int p = pixels;
bec80f4f
JS
654 double scale = 1.0;
655 if (GetBuffer())
656 scale = GetBuffer()->GetScale();
657
658 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
24777478
JS
659}
660
bec80f4f 661int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
24777478
JS
662{
663 // There are ppi pixels in 254.1 "1/10 mm"
bec80f4f
JS
664
665 double p = double(pixels);
666
667 if (scale != 1.0)
668 p *= scale;
669
670 int units = int( p * 254.1 / (double) ppi );
24777478
JS
671 return units;
672}
673
bec80f4f 674// Draw the borders and background for the given rectangle and attributes.
603f702b
JS
675// Width and height are taken to be the outer margin size, not the content.
676bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
bec80f4f
JS
677{
678 // Assume boxRect is the area around the content
603f702b
JS
679 wxRect marginRect = boxRect;
680 wxRect contentRect, borderRect, paddingRect, outlineRect;
bec80f4f 681
603f702b 682 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
bec80f4f
JS
683
684 // Margin is transparent. Draw background from margin.
603f702b 685 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
bec80f4f 686 {
603f702b
JS
687 wxColour colour;
688 if (flags & wxRICHTEXT_DRAW_SELECTED)
689 {
690 // TODO: get selection colour from control?
691 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
692 }
693 else
694 colour = attr.GetBackgroundColour();
695
696 wxPen pen(colour);
697 wxBrush brush(colour);
bec80f4f
JS
698
699 dc.SetPen(pen);
700 dc.SetBrush(brush);
37e7b783 701 dc.DrawRectangle(borderRect);
bec80f4f
JS
702 }
703
603f702b
JS
704 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
705 {
706 wxRichTextAttr editBorderAttr = attr;
707 // TODO: make guideline colour configurable
708 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
709 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
710 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
711
712 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
713 }
714
715 if (attr.GetTextBoxAttr().GetBorder().IsValid())
716 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
bec80f4f 717
603f702b
JS
718 if (attr.GetTextBoxAttr().GetOutline().IsValid())
719 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
bec80f4f
JS
720
721 return true;
722}
723
724// Draw a border
603f702b 725bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
bec80f4f
JS
726{
727 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
603f702b 728 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 729
603f702b 730 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
731 {
732 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
733 wxColour col(attr.GetLeft().GetColour());
734
735 // If pen width is > 1, resorts to a solid rectangle.
736 if (borderLeft == 1)
737 {
738 int penStyle = wxSOLID;
739 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
740 penStyle = wxDOT;
741 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
742 penStyle = wxLONG_DASH;
603f702b 743 wxPen pen(col, 1, penStyle);
bec80f4f
JS
744 dc.SetPen(pen);
745 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
746
747 }
748 else if (borderLeft > 1)
749 {
750 wxPen pen(col);
751 wxBrush brush(col);
752 dc.SetPen(pen);
753 dc.SetBrush(brush);
754 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
755 }
756 }
757
603f702b 758 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
759 {
760 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
761
762 wxColour col(attr.GetRight().GetColour());
763
764 // If pen width is > 1, resorts to a solid rectangle.
765 if (borderRight == 1)
766 {
767 int penStyle = wxSOLID;
768 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
769 penStyle = wxDOT;
770 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
771 penStyle = wxLONG_DASH;
603f702b 772 wxPen pen(col, 1, penStyle);
bec80f4f 773 dc.SetPen(pen);
603f702b 774 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
bec80f4f
JS
775
776 }
777 else if (borderRight > 1)
778 {
779 wxPen pen(col);
780 wxBrush brush(col);
781 dc.SetPen(pen);
782 dc.SetBrush(brush);
63af79de 783 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
bec80f4f
JS
784 }
785 }
786
603f702b 787 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
788 {
789 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
790
791 wxColour col(attr.GetTop().GetColour());
792
793 // If pen width is > 1, resorts to a solid rectangle.
794 if (borderTop == 1)
795 {
796 int penStyle = wxSOLID;
797 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
798 penStyle = wxDOT;
799 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
800 penStyle = wxLONG_DASH;
603f702b 801 wxPen pen(col, 1, penStyle);
bec80f4f
JS
802 dc.SetPen(pen);
803 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
804
805 }
806 else if (borderTop > 1)
807 {
808 wxPen pen(col);
809 wxBrush brush(col);
810 dc.SetPen(pen);
811 dc.SetBrush(brush);
812 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
813 }
814 }
815
603f702b 816 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
817 {
818 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
819 wxColour col(attr.GetTop().GetColour());
820
821 // If pen width is > 1, resorts to a solid rectangle.
822 if (borderBottom == 1)
823 {
824 int penStyle = wxSOLID;
825 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
826 penStyle = wxDOT;
827 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
828 penStyle = wxLONG_DASH;
603f702b 829 wxPen pen(col, 1, penStyle);
bec80f4f
JS
830 dc.SetPen(pen);
831 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
832
833 }
834 else if (borderBottom > 1)
835 {
836 wxPen pen(col);
837 wxBrush brush(col);
838 dc.SetPen(pen);
839 dc.SetBrush(brush);
63af79de 840 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
bec80f4f
JS
841 }
842 }
843
844 return true;
845}
846
847// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
848// or marginRect (outer), and the other must be the default rectangle (no width or height).
849// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
850// is available.
851//
852// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
853
603f702b 854bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
bec80f4f
JS
855{
856 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
857 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
858 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
859 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
860
603f702b 861 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 862
603f702b 863 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f 864 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
603f702b 865 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
bec80f4f 866 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
603f702b 867 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
bec80f4f 868 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
83c6ae8e 869 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
bec80f4f
JS
870 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
871
603f702b 872 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f 873 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
603f702b 874 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
bec80f4f 875 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
603f702b 876 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
bec80f4f 877 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
83c6ae8e 878 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
bec80f4f
JS
879 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
880
603f702b 881 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
bec80f4f 882 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
603f702b 883 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
bec80f4f 884 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
603f702b 885 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
bec80f4f 886 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b 887 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
bec80f4f
JS
888 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
889
603f702b 890 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
bec80f4f 891 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
603f702b 892 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
bec80f4f 893 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
603f702b 894 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
bec80f4f 895 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
603f702b 896 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
bec80f4f
JS
897 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
898
899 int leftTotal = marginLeft + borderLeft + paddingLeft;
900 int rightTotal = marginRight + borderRight + paddingRight;
901 int topTotal = marginTop + borderTop + paddingTop;
902 int bottomTotal = marginBottom + borderBottom + paddingBottom;
903
904 if (marginRect != wxRect())
905 {
906 contentRect.x = marginRect.x + leftTotal;
907 contentRect.y = marginRect.y + topTotal;
908 contentRect.width = marginRect.width - (leftTotal + rightTotal);
909 contentRect.height = marginRect.height - (topTotal + bottomTotal);
910 }
911 else
912 {
913 marginRect.x = contentRect.x - leftTotal;
914 marginRect.y = contentRect.y - topTotal;
915 marginRect.width = contentRect.width + (leftTotal + rightTotal);
916 marginRect.height = contentRect.height + (topTotal + bottomTotal);
917 }
918
919 borderRect.x = marginRect.x + marginLeft;
920 borderRect.y = marginRect.y + marginTop;
921 borderRect.width = marginRect.width - (marginLeft + marginRight);
922 borderRect.height = marginRect.height - (marginTop + marginBottom);
923
924 paddingRect.x = marginRect.x + marginLeft + borderLeft;
925 paddingRect.y = marginRect.y + marginTop + borderTop;
926 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
927 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
928
929 // The outline is outside the margin and doesn't influence the overall box position or content size.
930 outlineRect.x = marginRect.x - outlineLeft;
931 outlineRect.y = marginRect.y - outlineTop;
932 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
933 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
934
935 return true;
936}
937
603f702b
JS
938// Get the total margin for the object in pixels, taking into account margin, padding and border size
939bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
940 int& topMargin, int& bottomMargin)
941{
942 // Assume boxRect is the area around the content
943 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
944 marginRect = wxRect(0, 0, 1000, 1000);
bec80f4f 945
603f702b
JS
946 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
947
948 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
949 rightMargin = marginRect.GetRight() - contentRect.GetRight();
950 topMargin = contentRect.GetTop() - marginRect.GetTop();
951 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
952
953 return true;
954}
955
956// Returns the rectangle which the child has available to it given restrictions specified in the
957// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
bb7bbd12
JS
958// availableContainerSpace might be a parent that the cell has to compute its width relative to.
959// E.g. a cell that's 50% of its parent.
960wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
603f702b
JS
961{
962 wxRect rect = availableParentSpace;
963 double scale = 1.0;
964 if (buffer)
965 scale = buffer->GetScale();
966
bb7bbd12 967 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
603f702b
JS
968
969 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
970 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
971
972 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
973 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
974
975 // Can specify either left or right for the position (we're assuming we can't
976 // set the left and right edges to effectively set the size. Would we want to do that?)
977 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
978 {
979 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
980 }
981 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
982 {
983 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
984 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 985 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
603f702b
JS
986 else
987 rect.x += x;
988 }
989
990 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
991 {
992 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
993 }
994 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
995 {
996 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
997 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 998 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
603f702b
JS
999 else
1000 rect.y += y;
1001 }
1002
bb7bbd12
JS
1003 if (rect.GetWidth() > availableParentSpace.GetWidth())
1004 rect.SetWidth(availableParentSpace.GetWidth());
1005
603f702b
JS
1006 return rect;
1007}
1008
1009// Dump to output stream for debugging
5d7836c4
JS
1010void wxRichTextObject::Dump(wxTextOutputStream& stream)
1011{
1012 stream << GetClassInfo()->GetClassName() << wxT("\n");
1013 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");
1014 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");
1015}
1016
603f702b 1017// Gets the containing buffer
44219ff0
JS
1018wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1019{
1020 const wxRichTextObject* obj = this;
1021 while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer)))
1022 obj = obj->GetParent();
1023 return wxDynamicCast(obj, wxRichTextBuffer);
1024}
5d7836c4 1025
603f702b
JS
1026// Get the absolute object position, by traversing up the child/parent hierarchy
1027wxPoint wxRichTextObject::GetAbsolutePosition() const
1028{
1029 wxPoint pt = GetPosition();
1030
1031 wxRichTextObject* p = GetParent();
1032 while (p)
1033 {
1034 pt = pt + p->GetPosition();
1035 p = p->GetParent();
1036 }
1037
1038 return pt;
1039}
1040
1041// Hit-testing: returns a flag indicating hit test details, plus
1042// information about position
8db2e3ef 1043int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
603f702b
JS
1044{
1045 if (!IsShown())
1046 return wxRICHTEXT_HITTEST_NONE;
1047
1048 wxRect rect = GetRect();
1049 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1050 pt.y >= rect.y && pt.y < rect.y + rect.height)
1051 {
1052 *obj = this;
1053 *contextObj = GetParentContainer();
1054 textPosition = GetRange().GetStart();
1055 return wxRICHTEXT_HITTEST_ON;
1056 }
1057 else
1058 return wxRICHTEXT_HITTEST_NONE;
1059}
1060
1061// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1062// lays out the object again using the maximum ('best') size
8db2e3ef 1063bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
bb7bbd12
JS
1064 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1065 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
603f702b
JS
1066 int style)
1067{
bb7bbd12 1068 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
603f702b 1069 wxRect originalAvailableRect = availableChildRect;
8db2e3ef 1070 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1071
1072 wxSize maxSize = GetMaxSize();
1073
1074 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1075 // on this basis
bb7bbd12 1076 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
603f702b
JS
1077 {
1078 // Redo the layout with a fixed, minimum size this time.
1079 Invalidate(wxRICHTEXT_ALL);
1080 wxRichTextAttr newAttr(attr);
1081 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1082 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1083
bb7bbd12 1084 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
603f702b
JS
1085
1086 // If a paragraph, align the whole paragraph.
1087 // Problem with this: if we're limited by a floating object, a line may be centered
1088 // w.r.t. the smaller resulting box rather than the actual available width.
07d4142f 1089 if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
603f702b
JS
1090 {
1091 // centering, right-justification
8db2e3ef 1092 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
1093 {
1094 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1095 }
8db2e3ef 1096 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
1097 {
1098 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1099 }
1100 }
1101
8db2e3ef 1102 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1103 }
1104
1105 /*
1106 __________________
1107 | ____________ |
1108 | | | |
1109
1110
1111 */
1112
1113 return true;
1114}
1115
1116// Move the object recursively, by adding the offset from old to new
1117void wxRichTextObject::Move(const wxPoint& pt)
1118{
1119 SetPosition(pt);
1120}
1121
1122
5d7836c4
JS
1123/*!
1124 * wxRichTextCompositeObject
1125 * This is the base for drawable objects.
1126 */
1127
1128IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1129
1130wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1131 wxRichTextObject(parent)
1132{
1133}
1134
1135wxRichTextCompositeObject::~wxRichTextCompositeObject()
1136{
1137 DeleteChildren();
1138}
1139
1140/// Get the nth child
1141wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1142{
1143 wxASSERT ( n < m_children.GetCount() );
1144
1145 return m_children.Item(n)->GetData();
1146}
1147
1148/// Append a child, returning the position
1149size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1150{
1151 m_children.Append(child);
1152 child->SetParent(this);
1153 return m_children.GetCount() - 1;
1154}
1155
1156/// Insert the child in front of the given object, or at the beginning
1157bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1158{
1159 if (inFrontOf)
1160 {
1161 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1162 m_children.Insert(node, child);
1163 }
1164 else
1165 m_children.Insert(child);
1166 child->SetParent(this);
1167
1168 return true;
1169}
1170
1171/// Delete the child
1172bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1173{
1174 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1175 if (node)
1176 {
efbf6735
JS
1177 wxRichTextObject* obj = node->GetData();
1178 m_children.Erase(node);
5d7836c4 1179 if (deleteChild)
efbf6735 1180 delete obj;
5d7836c4
JS
1181
1182 return true;
1183 }
1184 return false;
1185}
1186
1187/// Delete all children
1188bool wxRichTextCompositeObject::DeleteChildren()
1189{
1190 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1191 while (node)
1192 {
1193 wxRichTextObjectList::compatibility_iterator oldNode = node;
1194
1195 wxRichTextObject* child = node->GetData();
1196 child->Dereference(); // Only delete if reference count is zero
1197
1198 node = node->GetNext();
efbf6735 1199 m_children.Erase(oldNode);
5d7836c4
JS
1200 }
1201
1202 return true;
1203}
1204
1205/// Get the child count
1206size_t wxRichTextCompositeObject::GetChildCount() const
1207{
1208 return m_children.GetCount();
1209}
1210
1211/// Copy
1212void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1213{
1214 wxRichTextObject::Copy(obj);
1215
1216 DeleteChildren();
1217
1218 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1219 while (node)
1220 {
1221 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
1222 wxRichTextObject* newChild = child->Clone();
1223 newChild->SetParent(this);
1224 m_children.Append(newChild);
5d7836c4
JS
1225
1226 node = node->GetNext();
1227 }
1228}
1229
1230/// Hit-testing: returns a flag indicating hit test details, plus
1231/// information about position
8db2e3ef 1232int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 1233{
603f702b
JS
1234 if (!IsShown())
1235 return wxRICHTEXT_HITTEST_NONE;
1236
5d7836c4
JS
1237 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1238 while (node)
1239 {
1240 wxRichTextObject* child = node->GetData();
1241
603f702b
JS
1242 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1243 {
1244 // Just check if we hit the overall object
8db2e3ef 1245 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1246 if (ret != wxRICHTEXT_HITTEST_NONE)
1247 return ret;
1248 }
1249 else if (child->IsShown())
1250 {
8db2e3ef 1251 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1252 if (ret != wxRICHTEXT_HITTEST_NONE)
1253 return ret;
1254 }
5d7836c4
JS
1255
1256 node = node->GetNext();
1257 }
1258
603f702b 1259 return wxRICHTEXT_HITTEST_NONE;
5d7836c4
JS
1260}
1261
1262/// Finds the absolute position and row height for the given character position
8db2e3ef 1263bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
1264{
1265 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1266 while (node)
1267 {
1268 wxRichTextObject* child = node->GetData();
1269
603f702b
JS
1270 // Don't recurse if the child is a top-level object,
1271 // such as a text box, because the character position will no longer
1272 // apply. By definition, a top-level object has its own range of
1273 // character positions.
8db2e3ef 1274 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
5d7836c4
JS
1275 return true;
1276
1277 node = node->GetNext();
1278 }
1279
1280 return false;
1281}
1282
1283/// Calculate range
1284void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1285{
1286 long current = start;
1287 long lastEnd = current;
1288
603f702b
JS
1289 if (IsTopLevel())
1290 {
1291 current = 0;
1292 lastEnd = 0;
1293 }
1294
5d7836c4
JS
1295 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1296 while (node)
1297 {
1298 wxRichTextObject* child = node->GetData();
1299 long childEnd = 0;
1300
1301 child->CalculateRange(current, childEnd);
1302 lastEnd = childEnd;
1303
1304 current = childEnd + 1;
1305
1306 node = node->GetNext();
1307 }
1308
603f702b
JS
1309 if (IsTopLevel())
1310 {
1311 // A top-level object always has a range of size 1,
1312 // because its children don't count at this level.
1313 end = start;
1314 m_range.SetRange(start, start);
5d7836c4 1315
603f702b
JS
1316 // An object with no children has zero length
1317 if (m_children.GetCount() == 0)
1318 lastEnd --;
1319 m_ownRange.SetRange(0, lastEnd);
1320 }
1321 else
1322 {
1323 end = lastEnd;
5d7836c4 1324
603f702b
JS
1325 // An object with no children has zero length
1326 if (m_children.GetCount() == 0)
1327 end --;
1328
1329 m_range.SetRange(start, end);
1330 }
5d7836c4
JS
1331}
1332
1333/// Delete range from layout.
1334bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1335{
1336 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1337
5d7836c4
JS
1338 while (node)
1339 {
1340 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1341 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1342
5d7836c4
JS
1343 // Delete the range in each paragraph
1344
1345 // When a chunk has been deleted, internally the content does not
1346 // now match the ranges.
1347 // However, so long as deletion is not done on the same object twice this is OK.
1348 // If you may delete content from the same object twice, recalculate
1349 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1350 // adjust the range you're deleting accordingly.
7fe8059f 1351
5d7836c4
JS
1352 if (!obj->GetRange().IsOutside(range))
1353 {
603f702b
JS
1354 // No need to delete within a top-level object; just removing this object will do fine
1355 if (!obj->IsTopLevel())
1356 obj->DeleteRange(range);
5d7836c4
JS
1357
1358 // Delete an empty object, or paragraph within this range.
1359 if (obj->IsEmpty() ||
1360 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1361 {
1362 // An empty paragraph has length 1, so won't be deleted unless the
1363 // whole range is deleted.
7fe8059f 1364 RemoveChild(obj, true);
5d7836c4
JS
1365 }
1366 }
7fe8059f 1367
5d7836c4
JS
1368 node = next;
1369 }
7fe8059f 1370
5d7836c4
JS
1371 return true;
1372}
1373
1374/// Get any text in this object for the given range
1375wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1376{
1377 wxString text;
1378 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1379 while (node)
1380 {
1381 wxRichTextObject* child = node->GetData();
1382 wxRichTextRange childRange = range;
1383 if (!child->GetRange().IsOutside(range))
1384 {
1385 childRange.LimitTo(child->GetRange());
7fe8059f 1386
5d7836c4 1387 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1388
5d7836c4
JS
1389 text += childText;
1390 }
1391 node = node->GetNext();
1392 }
1393
1394 return text;
1395}
1396
603f702b
JS
1397/// Get the child object at the given character position
1398wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1399{
1400 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1401 while (node)
1402 {
1403 wxRichTextObject* child = node->GetData();
1404 if (child->GetRange().GetStart() == pos)
1405 return child;
1406 node = node->GetNext();
1407 }
1408 return NULL;
1409}
1410
5d7836c4 1411/// Recursively merge all pieces that can be merged.
109bfc88 1412bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
5d7836c4
JS
1413{
1414 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1415 while (node)
1416 {
1417 wxRichTextObject* child = node->GetData();
5cb0b827 1418 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
5d7836c4 1419 {
109bfc88
JS
1420 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1421 if (composite)
1422 composite->Defragment();
1423
1424 if (node->GetNext())
5d7836c4 1425 {
109bfc88
JS
1426 wxRichTextObject* nextChild = node->GetNext()->GetData();
1427 if (child->CanMerge(nextChild) && child->Merge(nextChild))
1428 {
1429 nextChild->Dereference();
1430 m_children.Erase(node->GetNext());
5d7836c4 1431
109bfc88
JS
1432 // Don't set node -- we'll see if we can merge again with the next
1433 // child.
1434 }
1435 else
1436 node = node->GetNext();
5d7836c4
JS
1437 }
1438 else
1439 node = node->GetNext();
1440 }
1441 else
1442 node = node->GetNext();
1443 }
1444
bec80f4f
JS
1445 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1446 if (GetChildCount() > 1)
5d7836c4 1447 {
bec80f4f
JS
1448 node = m_children.GetFirst();
1449 while (node)
1450 {
1451 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1452 wxRichTextObject* child = node->GetData();
1453 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1454 {
1455 if (child->IsEmpty())
1456 {
1457 child->Dereference();
1458 m_children.Erase(node);
1459 }
1460 node = next;
1461 }
1462 else
1463 node = node->GetNext();
1464 }
5d7836c4 1465 }
5d7836c4 1466
5d7836c4
JS
1467 return true;
1468}
1469
bec80f4f
JS
1470/// Dump to output stream for debugging
1471void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1472{
1473 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1474 while (node)
1475 {
1476 wxRichTextObject* child = node->GetData();
bec80f4f 1477 child->Dump(stream);
5d7836c4
JS
1478 node = node->GetNext();
1479 }
5d7836c4
JS
1480}
1481
603f702b
JS
1482/// Get/set the object size for the given range. Returns false if the range
1483/// is invalid for this object.
8db2e3ef 1484bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b
JS
1485{
1486 if (!range.IsWithin(GetRange()))
1487 return false;
5d7836c4 1488
603f702b 1489 wxSize sz;
5d7836c4 1490
603f702b
JS
1491 wxArrayInt childExtents;
1492 wxArrayInt* p;
1493 if (partialExtents)
1494 p = & childExtents;
1495 else
1496 p = NULL;
5d7836c4 1497
603f702b
JS
1498 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1499 while (node)
cdaed652 1500 {
603f702b
JS
1501 wxRichTextObject* child = node->GetData();
1502 if (!child->GetRange().IsOutside(range))
1503 {
1504 // Floating objects have a zero size within the paragraph.
1505 if (child->IsFloating())
1506 {
1507 if (partialExtents)
1508 {
1509 int lastSize;
1510 if (partialExtents->GetCount() > 0)
1511 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1512 else
1513 lastSize = 0;
cdaed652 1514
603f702b
JS
1515 partialExtents->Add(0 /* zero size */ + lastSize);
1516 }
1517 }
1518 else
1519 {
1520 wxSize childSize;
5d7836c4 1521
603f702b
JS
1522 wxRichTextRange rangeToUse = range;
1523 rangeToUse.LimitTo(child->GetRange());
1524 if (child->IsTopLevel())
1525 rangeToUse = child->GetOwnRange();
5d7836c4 1526
603f702b 1527 int childDescent = 0;
cdaed652 1528
603f702b
JS
1529 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1530 // but it's only going to be used after caching has taken place.
1531 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1532 {
1533 childDescent = child->GetDescent();
1534 childSize = child->GetCachedSize();
bec80f4f 1535
603f702b
JS
1536 sz.y = wxMax(sz.y, childSize.y);
1537 sz.x += childSize.x;
1538 descent = wxMax(descent, childDescent);
1539 }
8db2e3ef 1540 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b
JS
1541 {
1542 sz.y = wxMax(sz.y, childSize.y);
1543 sz.x += childSize.x;
1544 descent = wxMax(descent, childDescent);
bec80f4f 1545
603f702b
JS
1546 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1547 {
1548 child->SetCachedSize(childSize);
1549 child->SetDescent(childDescent);
1550 }
bec80f4f 1551
603f702b
JS
1552 if (partialExtents)
1553 {
1554 int lastSize;
1555 if (partialExtents->GetCount() > 0)
1556 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1557 else
1558 lastSize = 0;
bec80f4f 1559
603f702b
JS
1560 size_t i;
1561 for (i = 0; i < childExtents.GetCount(); i++)
1562 {
1563 partialExtents->Add(childExtents[i] + lastSize);
1564 }
1565 }
1566 }
1567 }
1568
1569 if (p)
1570 p->Clear();
1571 }
1572
1573 node = node->GetNext();
1574 }
1575 size = sz;
1576 return true;
1577}
1578
1579// Invalidate the buffer. With no argument, invalidates whole buffer.
1580void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1581{
1582 wxRichTextObject::Invalidate(invalidRange);
1583
1584 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1585 while (node)
1586 {
1587 wxRichTextObject* child = node->GetData();
1588 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1589 {
1590 // Skip
1591 }
1592 else if (child->IsTopLevel())
1593 {
1594 if (invalidRange == wxRICHTEXT_NONE)
1595 child->Invalidate(wxRICHTEXT_NONE);
1596 else
1597 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1598 }
1599 else
1600 child->Invalidate(invalidRange);
1601 node = node->GetNext();
1602 }
1603}
1604
1605// Move the object recursively, by adding the offset from old to new
1606void wxRichTextCompositeObject::Move(const wxPoint& pt)
1607{
1608 wxPoint oldPos = GetPosition();
1609 SetPosition(pt);
1610 wxPoint offset = pt - oldPos;
1611
1612 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1613 while (node)
1614 {
1615 wxRichTextObject* child = node->GetData();
1616 wxPoint childPos = child->GetPosition() + offset;
1617 child->Move(childPos);
1618 node = node->GetNext();
1619 }
1620}
1621
1622
1623/*!
1624 * wxRichTextParagraphLayoutBox
1625 * This box knows how to lay out paragraphs.
1626 */
1627
1628IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1629
1630wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1631 wxRichTextCompositeObject(parent)
1632{
1633 Init();
1634}
1635
1636wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1637{
1638 if (m_floatCollector)
1639 {
1640 delete m_floatCollector;
1641 m_floatCollector = NULL;
1642 }
1643}
1644
1645/// Initialize the object.
1646void wxRichTextParagraphLayoutBox::Init()
1647{
1648 m_ctrl = NULL;
1649
1650 // For now, assume is the only box and has no initial size.
1651 m_range = wxRichTextRange(0, -1);
1652 m_ownRange = wxRichTextRange(0, -1);
1653
1654 m_invalidRange = wxRICHTEXT_ALL;
1655
1656 SetMargins(4);
1657 m_partialParagraph = false;
1658 m_floatCollector = NULL;
1659}
1660
1661void wxRichTextParagraphLayoutBox::Clear()
1662{
1663 DeleteChildren();
1664
1665 if (m_floatCollector)
1666 delete m_floatCollector;
1667 m_floatCollector = NULL;
1668 m_partialParagraph = false;
1669}
1670
1671/// Copy
1672void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1673{
1674 Clear();
1675
1676 wxRichTextCompositeObject::Copy(obj);
1677
1678 m_partialParagraph = obj.m_partialParagraph;
1679 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1680}
1681
07d4142f
JS
1682// Gather information about floating objects; only gather floats for those paragraphs that
1683// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1684// during layout.
603f702b 1685bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1686{
1687 if (m_floatCollector != NULL)
1688 delete m_floatCollector;
603f702b 1689 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652 1690 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
07d4142f
JS
1691 // Only gather floats up to the point we'll start formatting paragraphs.
1692 while (untilObj && node && node->GetData() != untilObj)
cdaed652
VZ
1693 {
1694 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1695 wxASSERT (child != NULL);
1696 if (child)
1697 m_floatCollector->CollectFloat(child);
1698 node = node->GetNext();
1699 }
ce00f59b 1700
cdaed652
VZ
1701 return true;
1702}
1703
603f702b
JS
1704// Returns the style sheet associated with the overall buffer.
1705wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1706{
1707 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1708}
1709
1710// Get the number of floating objects at this level
1711int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1712{
1713 if (m_floatCollector)
1714 return m_floatCollector->GetFloatingObjectCount();
1715 else
1716 return 0;
1717}
1718
1719// Get a list of floating objects
1720bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1721{
1722 if (m_floatCollector)
1723 {
1724 return m_floatCollector->GetFloatingObjects(objects);
1725 }
1726 else
1727 return false;
1728}
1729
1730// Calculate ranges
1731void wxRichTextParagraphLayoutBox::UpdateRanges()
1732{
1733 long start = 0;
1734 if (GetParent())
1735 start = GetRange().GetStart();
1736 long end;
1737 CalculateRange(start, end);
1738}
1739
cdaed652 1740// HitTest
8db2e3ef 1741int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1742{
603f702b
JS
1743 if (!IsShown())
1744 return wxRICHTEXT_HITTEST_NONE;
1745
cdaed652 1746 int ret = wxRICHTEXT_HITTEST_NONE;
343ef639 1747 if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
8db2e3ef 1748 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
ce00f59b 1749
cdaed652 1750 if (ret == wxRICHTEXT_HITTEST_NONE)
8db2e3ef 1751 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
cdaed652 1752 else
603f702b
JS
1753 {
1754 *contextObj = this;
cdaed652 1755 return ret;
603f702b 1756 }
cdaed652
VZ
1757}
1758
1759/// Draw the floating objects
8db2e3ef 1760void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
1761{
1762 if (m_floatCollector)
8db2e3ef 1763 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
cdaed652
VZ
1764}
1765
bec80f4f 1766void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1767{
1768 if (from == to)
1769 return;
1770
1771 from->RemoveChild(obj);
1772 to->AppendChild(obj);
5d7836c4
JS
1773}
1774
1775/// Draw the item
8db2e3ef 1776bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1777{
603f702b
JS
1778 if (!IsShown())
1779 return true;
1780
1781 wxRect thisRect(GetPosition(), GetCachedSize());
1782
8db2e3ef
JS
1783 wxRichTextAttr attr(GetAttributes());
1784 context.ApplyVirtualAttributes(attr, this);
1785
603f702b
JS
1786 int flags = style;
1787 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1788 flags |= wxRICHTEXT_DRAW_SELECTED;
1789
1790 // Don't draw guidelines if at top level
1791 int theseFlags = flags;
1792 if (!GetParent())
1793 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
8db2e3ef 1794 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
603f702b 1795
8db2e3ef 1796 DrawFloats(dc, context, range, selection, rect, descent, style);
5d7836c4
JS
1797 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1798 while (node)
1799 {
603f702b 1800 wxRichTextObject* child = node->GetData();
7fe8059f 1801
5d7836c4
JS
1802 if (child && !child->GetRange().IsOutside(range))
1803 {
1804 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1805 wxRichTextRange childRange = range;
1806 if (child->IsTopLevel())
1807 {
1808 childRange = child->GetOwnRange();
1809 }
7fe8059f 1810
ea160b2e
JS
1811 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1812 {
1813 // Stop drawing
1814 break;
1815 }
1816 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1817 {
1818 // Skip
1819 }
1820 else
8db2e3ef 1821 child->Draw(dc, context, childRange, selection, rect, descent, style);
5d7836c4
JS
1822 }
1823
1824 node = node->GetNext();
1825 }
1826 return true;
1827}
1828
1829/// Lay the item out
8db2e3ef 1830bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 1831{
603f702b
JS
1832 SetPosition(rect.GetPosition());
1833
1834 if (!IsShown())
1835 return true;
1836
4d551ad5
JS
1837 wxRect availableSpace;
1838 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1839
8db2e3ef
JS
1840 wxRichTextAttr attr(GetAttributes());
1841 context.ApplyVirtualAttributes(attr, this);
1842
4d551ad5 1843 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1844 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1845 // so that during a size, only the visible part will be relaid out, or
1846 // it would take too long causing flicker. As an approximation, we assume that
1847 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1848 if (formatRect)
1849 {
603f702b 1850 wxRect rect2(0, 0, rect.width, rect.height);
8db2e3ef 1851 availableSpace = GetAvailableContentArea(dc, context, rect2);
4d551ad5
JS
1852
1853 // Invalidate the part of the buffer from the first visible line
1854 // to the end. If other parts of the buffer are currently invalid,
1855 // then they too will be taken into account if they are above
1856 // the visible point.
1857 long startPos = 0;
1858 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1859 if (line)
1860 startPos = line->GetAbsoluteRange().GetStart();
1861
603f702b 1862 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1863 }
1864 else
603f702b 1865 {
8db2e3ef 1866 availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
1867 }
1868
1869 int leftMargin, rightMargin, topMargin, bottomMargin;
8db2e3ef 1870 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
603f702b 1871 topMargin, bottomMargin);
5d7836c4
JS
1872
1873 int maxWidth = 0;
603f702b
JS
1874 int maxHeight = 0;
1875
1876 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1877 int maxMaxWidth = 0;
1878
1879 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1880 int maxMinWidth = 0;
1881
1882 // If we have vertical alignment, we must recalculate everything.
8db2e3ef
JS
1883 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1884 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1885
5d7836c4 1886 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1887
38113684 1888 bool layoutAll = true;
1e967276 1889
38113684
JS
1890 // Get invalid range, rounding to paragraph start/end.
1891 wxRichTextRange invalidRange = GetInvalidRange(true);
1892
4d551ad5 1893 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1894 return true;
1895
603f702b 1896 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1897 layoutAll = true;
38113684 1898 else // If we know what range is affected, start laying out from that point on.
603f702b 1899 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1900 {
38113684 1901 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1902 if (firstParagraph)
1903 {
1904 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1905 wxRichTextObjectList::compatibility_iterator previousNode;
1906 if ( firstNode )
1907 previousNode = firstNode->GetPrevious();
9b4af7b7 1908 if (firstNode)
2c375f42 1909 {
9b4af7b7
JS
1910 if (previousNode)
1911 {
1912 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1913 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1914 }
7fe8059f 1915
2c375f42
JS
1916 // Now we're going to start iterating from the first affected paragraph.
1917 node = firstNode;
1e967276
JS
1918
1919 layoutAll = false;
2c375f42
JS
1920 }
1921 }
1922 }
1923
07d4142f
JS
1924 // Gather information about only those floating objects that will not be formatted,
1925 // after which floats will be gathered per-paragraph during layout.
603f702b 1926 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1927
4d551ad5
JS
1928 // A way to force speedy rest-of-buffer layout (the 'else' below)
1929 bool forceQuickLayout = false;
39a1c2f2 1930
d3f6b1b5
JS
1931 // First get the size of the paragraphs we won't be laying out
1932 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1933 while (n && n != node)
1934 {
1935 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
1936 if (child)
1937 {
1938 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1939 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1940 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1941 }
1942 n = n->GetNext();
1943 }
1944
5d7836c4
JS
1945 while (node)
1946 {
1947 // Assume this box only contains paragraphs
1948
1949 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
706465df
JS
1950 // Unsure if this is needed
1951 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 1952
603f702b 1953 if (child && child->IsShown())
2c375f42 1954 {
603f702b
JS
1955 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1956 if ( !forceQuickLayout &&
1957 (layoutAll ||
1958 child->GetLines().IsEmpty() ||
1959 !child->GetRange().IsOutside(invalidRange)) )
1960 {
1961 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1962 // lays out the object again using the minimum size
8db2e3ef
JS
1963 child->LayoutToBestSize(dc, context, GetBuffer(),
1964 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
1965
1966 // Layout must set the cached size
1967 availableSpace.y += child->GetCachedSize().y;
1968 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1969 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1970 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1971
1972 // If we're just formatting the visible part of the buffer,
1973 // and we're now past the bottom of the window, and we don't have any
1974 // floating objects (since they may cause wrapping to change for the rest of the
1975 // the buffer), start quick layout.
1976 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
1977 forceQuickLayout = true;
1978 }
1979 else
1980 {
1981 // We're outside the immediately affected range, so now let's just
1982 // move everything up or down. This assumes that all the children have previously
1983 // been laid out and have wrapped line lists associated with them.
1984 // TODO: check all paragraphs before the affected range.
1985
1986 int inc = availableSpace.y - child->GetPosition().y;
1987
1988 while (node)
1989 {
1990 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1991 if (child)
1992 {
1993 if (child->GetLines().GetCount() == 0)
1994 {
1995 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1996 // lays out the object again using the minimum size
8db2e3ef
JS
1997 child->LayoutToBestSize(dc, context, GetBuffer(),
1998 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
1999
2000 //child->Layout(dc, availableChildRect, style);
2001 }
2002 else
2003 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 2004
603f702b
JS
2005 availableSpace.y += child->GetCachedSize().y;
2006 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2007 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2008 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2009 }
4d551ad5 2010
603f702b
JS
2011 node = node->GetNext();
2012 }
2013 break;
2014 }
2c375f42 2015 }
7fe8059f 2016
603f702b
JS
2017 node = node->GetNext();
2018 }
2019
2020 node = m_children.GetLast();
2021 if (node && node->GetData()->IsShown())
2022 {
2023 wxRichTextObject* child = node->GetData();
603f702b
JS
2024 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2025 }
2026 else
2027 maxHeight = 0; // topMargin + bottomMargin;
2028
8db2e3ef 2029 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
bb7bbd12 2030 {
8db2e3ef 2031 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
bb7bbd12
JS
2032 int w = r.GetWidth();
2033
2034 // Convert external to content rect
2035 w = w - leftMargin - rightMargin;
2036 maxWidth = wxMax(maxWidth, w);
2037 maxMaxWidth = wxMax(maxMaxWidth, w);
2038 }
2039
603f702b
JS
2040 // TODO: (also in para layout) should set the
2041 // object's size to an absolute one if specified,
2042 // but if not specified, calculate it from content.
2043
2044 // We need to add back the margins etc.
2045 {
2046 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2047 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
8db2e3ef 2048 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2049 SetCachedSize(marginRect.GetSize());
2050 }
2051
2052 // The maximum size is the greatest of all maximum widths for all paragraphs.
2053 {
2054 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2055 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2056 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2057 SetMaxSize(marginRect.GetSize());
2058 }
2059
2060 // The minimum size is the greatest of all minimum widths for all paragraphs.
2061 {
2062 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2063 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2064 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2065 SetMinSize(marginRect.GetSize());
2066 }
2067
8db2e3ef
JS
2068 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2069 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
603f702b
JS
2070 {
2071 int yOffset = 0;
2072 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2073 if (leftOverSpace > 0)
2074 {
8db2e3ef 2075 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
603f702b
JS
2076 {
2077 yOffset = (leftOverSpace/2);
2078 }
8db2e3ef 2079 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
603f702b
JS
2080 {
2081 yOffset = leftOverSpace;
2082 }
2083 }
7fe8059f 2084
603f702b
JS
2085 // Move all the children to vertically align the content
2086 // This doesn't take into account floating objects, unfortunately.
2087 if (yOffset != 0)
2088 {
2089 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
2090 while (node)
2091 {
2092 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2093 if (child)
603f702b 2094 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
2095
2096 node = node->GetNext();
2c375f42 2097 }
2c375f42 2098 }
5d7836c4
JS
2099 }
2100
1e967276 2101 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2102
2103 return true;
2104}
2105
5d7836c4 2106/// Get/set the size for the given range.
8db2e3ef 2107bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
5d7836c4
JS
2108{
2109 wxSize sz;
2110
09f14108
JS
2111 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2112 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2113
2114 // First find the first paragraph whose starting position is within the range.
2115 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2116 while (node)
2117 {
2118 // child is a paragraph
2119 wxRichTextObject* child = node->GetData();
2120 const wxRichTextRange& r = child->GetRange();
2121
2122 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2123 {
2124 startPara = node;
2125 break;
2126 }
2127
2128 node = node->GetNext();
2129 }
2130
2131 // Next find the last paragraph containing part of the range
2132 node = m_children.GetFirst();
2133 while (node)
2134 {
2135 // child is a paragraph
2136 wxRichTextObject* child = node->GetData();
2137 const wxRichTextRange& r = child->GetRange();
2138
2139 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2140 {
2141 endPara = node;
2142 break;
2143 }
2144
2145 node = node->GetNext();
2146 }
2147
2148 if (!startPara || !endPara)
2149 return false;
2150
2151 // Now we can add up the sizes
2152 for (node = startPara; node ; node = node->GetNext())
2153 {
2154 // child is a paragraph
2155 wxRichTextObject* child = node->GetData();
2156 const wxRichTextRange& childRange = child->GetRange();
2157 wxRichTextRange rangeToFind = range;
2158 rangeToFind.LimitTo(childRange);
2159
603f702b
JS
2160 if (child->IsTopLevel())
2161 rangeToFind = child->GetOwnRange();
2162
5d7836c4
JS
2163 wxSize childSize;
2164
2165 int childDescent = 0;
8db2e3ef 2166 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
5d7836c4
JS
2167
2168 descent = wxMax(childDescent, descent);
2169
2170 sz.x = wxMax(sz.x, childSize.x);
2171 sz.y += childSize.y;
2172
2173 if (node == endPara)
2174 break;
2175 }
2176
2177 size = sz;
2178
2179 return true;
2180}
2181
2182/// Get the paragraph at the given position
2183wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2184{
2185 if (caretPosition)
2186 pos ++;
2187
2188 // First find the first paragraph whose starting position is within the range.
2189 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2190 while (node)
2191 {
2192 // child is a paragraph
2193 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2194 // wxASSERT (child != NULL);
5d7836c4 2195
603f702b
JS
2196 if (child)
2197 {
2198 // Return first child in buffer if position is -1
2199 // if (pos == -1)
2200 // return child;
5d7836c4 2201
603f702b
JS
2202 if (child->GetRange().Contains(pos))
2203 return child;
2204 }
5d7836c4
JS
2205
2206 node = node->GetNext();
2207 }
2208 return NULL;
2209}
2210
2211/// Get the line at the given position
2212wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2213{
2214 if (caretPosition)
2215 pos ++;
2216
2217 // First find the first paragraph whose starting position is within the range.
2218 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2219 while (node)
2220 {
7051fa41
JS
2221 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2222 if (obj->GetRange().Contains(pos))
5d7836c4 2223 {
7051fa41
JS
2224 // child is a paragraph
2225 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2226 // wxASSERT (child != NULL);
7051fa41 2227
603f702b 2228 if (child)
7051fa41 2229 {
603f702b
JS
2230 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2231 while (node2)
2232 {
2233 wxRichTextLine* line = node2->GetData();
5d7836c4 2234
603f702b 2235 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2236
603f702b 2237 if (range.Contains(pos) ||
5d7836c4 2238
603f702b
JS
2239 // If the position is end-of-paragraph, then return the last line of
2240 // of the paragraph.
2241 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2242 return line;
5d7836c4 2243
603f702b
JS
2244 node2 = node2->GetNext();
2245 }
7051fa41 2246 }
7fe8059f 2247 }
5d7836c4
JS
2248
2249 node = node->GetNext();
2250 }
2251
2252 int lineCount = GetLineCount();
2253 if (lineCount > 0)
2254 return GetLineForVisibleLineNumber(lineCount-1);
2255 else
2256 return NULL;
2257}
2258
2259/// Get the line at the given y pixel position, or the last line.
2260wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2261{
2262 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2263 while (node)
2264 {
2265 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2266 // wxASSERT (child != NULL);
5d7836c4 2267
603f702b 2268 if (child)
5d7836c4 2269 {
603f702b
JS
2270 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2271 while (node2)
2272 {
2273 wxRichTextLine* line = node2->GetData();
5d7836c4 2274
603f702b 2275 wxRect rect(line->GetRect());
5d7836c4 2276
603f702b
JS
2277 if (y <= rect.GetBottom())
2278 return line;
5d7836c4 2279
603f702b
JS
2280 node2 = node2->GetNext();
2281 }
7fe8059f 2282 }
5d7836c4
JS
2283
2284 node = node->GetNext();
2285 }
2286
2287 // Return last line
2288 int lineCount = GetLineCount();
2289 if (lineCount > 0)
2290 return GetLineForVisibleLineNumber(lineCount-1);
2291 else
2292 return NULL;
2293}
2294
2295/// Get the number of visible lines
2296int wxRichTextParagraphLayoutBox::GetLineCount() const
2297{
2298 int count = 0;
2299
2300 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2301 while (node)
2302 {
2303 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2304 // wxASSERT (child != NULL);
2305
2306 if (child)
2307 count += child->GetLines().GetCount();
5d7836c4 2308
5d7836c4
JS
2309 node = node->GetNext();
2310 }
2311 return count;
2312}
2313
2314
2315/// Get the paragraph for a given line
2316wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2317{
1e967276 2318 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2319}
2320
2321/// Get the line size at the given position
2322wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2323{
2324 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2325 if (line)
2326 {
2327 return line->GetSize();
2328 }
2329 else
2330 return wxSize(0, 0);
2331}
2332
2333
2334/// Convenience function to add a paragraph of text
24777478 2335wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2336{
fe5aa22c 2337 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2338 // be combined at display time.
2339 // Divide into paragraph and character styles.
3e541562 2340
24777478
JS
2341 wxRichTextAttr defaultCharStyle;
2342 wxRichTextAttr defaultParaStyle;
4f32b3cf 2343
5607c890
JS
2344 // If the default style is a named paragraph style, don't apply any character formatting
2345 // to the initial text string.
2346 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2347 {
2348 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2349 if (def)
2350 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2351 }
2352 else
2353 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2354
24777478
JS
2355 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2356 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2357
2358 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
5d7836c4
JS
2359
2360 AppendChild(para);
2361
2362 UpdateRanges();
5d7836c4
JS
2363
2364 return para->GetRange();
2365}
2366
2367/// Adds multiple paragraphs, based on newlines.
24777478 2368wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2369{
fe5aa22c 2370 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2371 // be combined at display time.
2372 // Divide into paragraph and character styles.
3e541562 2373
24777478
JS
2374 wxRichTextAttr defaultCharStyle;
2375 wxRichTextAttr defaultParaStyle;
5607c890
JS
2376
2377 // If the default style is a named paragraph style, don't apply any character formatting
2378 // to the initial text string.
2379 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2380 {
2381 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2382 if (def)
2383 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2384 }
2385 else
2386 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2387
24777478
JS
2388 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2389 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2390
5d7836c4
JS
2391 wxRichTextParagraph* firstPara = NULL;
2392 wxRichTextParagraph* lastPara = NULL;
2393
2394 wxRichTextRange range(-1, -1);
0ca07313 2395
5d7836c4 2396 size_t i = 0;
28f92d74 2397 size_t len = text.length();
5d7836c4 2398 wxString line;
4f32b3cf 2399 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
0ca07313
JS
2400
2401 AppendChild(para);
2402
2403 firstPara = para;
2404 lastPara = para;
2405
5d7836c4
JS
2406 while (i < len)
2407 {
2408 wxChar ch = text[i];
2409 if (ch == wxT('\n') || ch == wxT('\r'))
2410 {
99404ab0
JS
2411 if (i != (len-1))
2412 {
2413 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2414 plainText->SetText(line);
0ca07313 2415
99404ab0 2416 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
5d7836c4 2417
99404ab0 2418 AppendChild(para);
0ca07313 2419
99404ab0
JS
2420 lastPara = para;
2421 line = wxEmptyString;
2422 }
5d7836c4
JS
2423 }
2424 else
2425 line += ch;
2426
2427 i ++;
2428 }
0ca07313 2429
7fe8059f 2430 if (!line.empty())
5d7836c4 2431 {
0ca07313
JS
2432 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2433 plainText->SetText(line);
5d7836c4
JS
2434 }
2435
5d7836c4 2436 UpdateRanges();
0ca07313 2437
0ca07313 2438 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2439}
2440
2441/// Convenience function to add an image
24777478 2442wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2443{
fe5aa22c 2444 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2445 // be combined at display time.
2446 // Divide into paragraph and character styles.
3e541562 2447
24777478
JS
2448 wxRichTextAttr defaultCharStyle;
2449 wxRichTextAttr defaultParaStyle;
5607c890
JS
2450
2451 // If the default style is a named paragraph style, don't apply any character formatting
2452 // to the initial text string.
2453 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2454 {
2455 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2456 if (def)
2457 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2458 }
2459 else
2460 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2461
24777478
JS
2462 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2463 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2464
4f32b3cf
JS
2465 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2466 AppendChild(para);
2467 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2468
5d7836c4 2469 UpdateRanges();
5d7836c4
JS
2470
2471 return para->GetRange();
2472}
2473
2474
2475/// Insert fragment into this box at the given position. If partialParagraph is true,
2476/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2477/// marker.
5d7836c4 2478
0ca07313 2479bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2480{
5d7836c4
JS
2481 // First, find the first paragraph whose starting position is within the range.
2482 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2483 if (para)
2484 {
24777478 2485 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2486
5d7836c4
JS
2487 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2488
2489 // Now split at this position, returning the object to insert the new
2490 // ones in front of.
2491 wxRichTextObject* nextObject = para->SplitAt(position);
2492
2493 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2494 // text, for example, so let's optimize.
2495
2496 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2497 {
2498 // Add the first para to this para...
2499 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2500 if (!firstParaNode)
2501 return false;
2502
2503 // Iterate through the fragment paragraph inserting the content into this paragraph.
2504 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2505 wxASSERT (firstPara != NULL);
2506
2507 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2508 while (objectNode)
2509 {
2510 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2511
5d7836c4
JS
2512 if (!nextObject)
2513 {
2514 // Append
2515 para->AppendChild(newObj);
2516 }
2517 else
2518 {
2519 // Insert before nextObject
2520 para->InsertChild(newObj, nextObject);
2521 }
7fe8059f 2522
5d7836c4
JS
2523 objectNode = objectNode->GetNext();
2524 }
2525
2526 return true;
2527 }
2528 else
2529 {
2530 // Procedure for inserting a fragment consisting of a number of
2531 // paragraphs:
2532 //
2533 // 1. Remove and save the content that's after the insertion point, for adding
2534 // back once we've added the fragment.
2535 // 2. Add the content from the first fragment paragraph to the current
2536 // paragraph.
2537 // 3. Add remaining fragment paragraphs after the current paragraph.
2538 // 4. Add back the saved content from the first paragraph. If partialParagraph
2539 // is true, add it to the last paragraph added and not a new one.
2540
2541 // 1. Remove and save objects after split point.
2542 wxList savedObjects;
2543 if (nextObject)
2544 para->MoveToList(nextObject, savedObjects);
2545
2546 // 2. Add the content from the 1st fragment paragraph.
2547 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2548 if (!firstParaNode)
2549 return false;
2550
2551 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2552 wxASSERT(firstPara != NULL);
2553
6c0ea513
JS
2554 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2555 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2556
2557 // Save empty paragraph attributes for appending later
2558 // These are character attributes deliberately set for a new paragraph. Without this,
2559 // we couldn't pass default attributes when appending a new paragraph.
24777478 2560 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2561
5d7836c4 2562 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2563
2564 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2565 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2566
5d7836c4
JS
2567 while (objectNode)
2568 {
c025e094 2569 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2570
c025e094
JS
2571 // Append
2572 para->AppendChild(newObj);
7fe8059f 2573
5d7836c4
JS
2574 objectNode = objectNode->GetNext();
2575 }
2576
2577 // 3. Add remaining fragment paragraphs after the current paragraph.
2578 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2579 wxRichTextObject* nextParagraph = NULL;
2580 if (nextParagraphNode)
2581 nextParagraph = nextParagraphNode->GetData();
2582
2583 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2584 wxRichTextParagraph* finalPara = para;
2585
99404ab0
JS
2586 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2587
5d7836c4 2588 // If there was only one paragraph, we need to insert a new one.
99404ab0 2589 while (i)
5d7836c4 2590 {
99404ab0
JS
2591 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2592 wxASSERT( para != NULL );
5d7836c4 2593
99404ab0 2594 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2595
2596 if (nextParagraph)
2597 InsertChild(finalPara, nextParagraph);
2598 else
7fe8059f 2599 AppendChild(finalPara);
99404ab0
JS
2600
2601 i = i->GetNext();
5d7836c4 2602 }
5d7836c4 2603
99404ab0
JS
2604 // If there was only one paragraph, or we have full paragraphs in our fragment,
2605 // we need to insert a new one.
2606 if (needExtraPara)
2607 {
2608 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2609
2610 if (nextParagraph)
2611 InsertChild(finalPara, nextParagraph);
2612 else
2613 AppendChild(finalPara);
5d7836c4
JS
2614 }
2615
2616 // 4. Add back the remaining content.
2617 if (finalPara)
2618 {
c025e094
JS
2619 if (nextObject)
2620 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2621
2622 // Ensure there's at least one object
2623 if (finalPara->GetChildCount() == 0)
2624 {
7fe8059f 2625 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2626 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2627
2628 finalPara->AppendChild(text);
2629 }
2630 }
2631
6c0ea513
JS
2632 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2633 finalPara->SetAttributes(firstPara->GetAttributes());
2634 else if (finalPara && finalPara != para)
99404ab0
JS
2635 finalPara->SetAttributes(originalAttr);
2636
5d7836c4
JS
2637 return true;
2638 }
2639 }
2640 else
2641 {
2642 // Append
2643 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2644 while (i)
2645 {
2646 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2647 wxASSERT( para != NULL );
7fe8059f 2648
5d7836c4 2649 AppendChild(para->Clone());
7fe8059f 2650
5d7836c4
JS
2651 i = i->GetNext();
2652 }
2653
2654 return true;
2655 }
5d7836c4
JS
2656}
2657
2658/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2659/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2660bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2661{
2662 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2663 while (i)
2664 {
2665 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2666 wxASSERT( para != NULL );
2667
2668 if (!para->GetRange().IsOutside(range))
2669 {
2670 fragment.AppendChild(para->Clone());
7fe8059f 2671 }
5d7836c4
JS
2672 i = i->GetNext();
2673 }
2674
2675 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2676 if (!fragment.IsEmpty())
2677 {
5d7836c4
JS
2678 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2679 wxASSERT( firstPara != NULL );
2680
0e190fa2
JS
2681 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2682 wxASSERT( lastPara != NULL );
2683
2684 if (!firstPara || !lastPara)
2685 return false;
2686
2687 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2688
2689 long firstPos = firstPara->GetRange().GetStart();
2690
2691 // Adjust for renumbering from zero
2692 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2693
2694 long end;
2695 fragment.CalculateRange(0, end);
2696
5d7836c4 2697 // Chop off the start of the paragraph
0e190fa2 2698 if (topTailRange.GetStart() > 0)
5d7836c4 2699 {
0e190fa2 2700 wxRichTextRange r(0, topTailRange.GetStart()-1);
5d7836c4
JS
2701 firstPara->DeleteRange(r);
2702
2703 // Make sure the numbering is correct
0e190fa2 2704 fragment.CalculateRange(0, end);
5d7836c4
JS
2705
2706 // Now, we've deleted some positions, so adjust the range
2707 // accordingly.
0e190fa2
JS
2708 topTailRange.SetStart(range.GetLength());
2709 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2710 }
2711 else
2712 {
2713 topTailRange.SetStart(range.GetLength());
2714 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
5d7836c4
JS
2715 }
2716
61e6149e 2717 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
5d7836c4 2718 {
0e190fa2 2719 lastPara->DeleteRange(topTailRange);
5d7836c4
JS
2720
2721 // Make sure the numbering is correct
2722 long end;
0e190fa2 2723 fragment.CalculateRange(0, end);
5d7836c4
JS
2724
2725 // We only have part of a paragraph at the end
2726 fragment.SetPartialParagraph(true);
2727 }
2728 else
2729 {
0e190fa2
JS
2730 // We have a partial paragraph (don't save last new paragraph marker)
2731 // or complete paragraph
2732 fragment.SetPartialParagraph(isFragment);
5d7836c4
JS
2733 }
2734 }
2735
2736 return true;
2737}
2738
2739/// Given a position, get the number of the visible line (potentially many to a paragraph),
2740/// starting from zero at the start of the buffer.
2741long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2742{
2743 if (caretPosition)
2744 pos ++;
2745
2746 int lineCount = 0;
2747
2748 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2749 while (node)
2750 {
2751 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2752 // wxASSERT( child != NULL );
5d7836c4 2753
603f702b 2754 if (child)
5d7836c4 2755 {
603f702b 2756 if (child->GetRange().Contains(pos))
5d7836c4 2757 {
603f702b
JS
2758 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2759 while (node2)
5d7836c4 2760 {
603f702b
JS
2761 wxRichTextLine* line = node2->GetData();
2762 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2763
603f702b
JS
2764 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2765 {
2766 // If the caret is displayed at the end of the previous wrapped line,
2767 // we want to return the line it's _displayed_ at (not the actual line
2768 // containing the position).
2769 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2770 return lineCount - 1;
2771 else
2772 return lineCount;
2773 }
7fe8059f 2774
603f702b
JS
2775 lineCount ++;
2776
2777 node2 = node2->GetNext();
2778 }
2779 // If we didn't find it in the lines, it must be
2780 // the last position of the paragraph. So return the last line.
2781 return lineCount-1;
5d7836c4 2782 }
603f702b
JS
2783 else
2784 lineCount += child->GetLines().GetCount();
5d7836c4 2785 }
5d7836c4
JS
2786
2787 node = node->GetNext();
2788 }
2789
2790 // Not found
2791 return -1;
2792}
2793
2794/// Given a line number, get the corresponding wxRichTextLine object.
2795wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2796{
2797 int lineCount = 0;
2798
2799 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2800 while (node)
2801 {
2802 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2803 // wxASSERT(child != NULL);
5d7836c4 2804
603f702b 2805 if (child)
5d7836c4 2806 {
603f702b 2807 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2808 {
603f702b
JS
2809 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2810 while (node2)
2811 {
2812 wxRichTextLine* line = node2->GetData();
7fe8059f 2813
603f702b
JS
2814 if (lineCount == lineNumber)
2815 return line;
5d7836c4 2816
603f702b 2817 lineCount ++;
7fe8059f 2818
603f702b
JS
2819 node2 = node2->GetNext();
2820 }
7fe8059f 2821 }
603f702b
JS
2822 else
2823 lineCount += child->GetLines().GetCount();
5d7836c4 2824 }
5d7836c4
JS
2825
2826 node = node->GetNext();
2827 }
2828
2829 // Didn't find it
2830 return NULL;
2831}
2832
2833/// Delete range from layout.
2834bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2835{
2836 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2837
99404ab0 2838 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2839 while (node)
2840 {
2841 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2842 // wxASSERT (obj != NULL);
5d7836c4
JS
2843
2844 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2845
603f702b 2846 if (obj)
5d7836c4 2847 {
603f702b 2848 // Delete the range in each paragraph
99404ab0 2849
603f702b 2850 if (!obj->GetRange().IsOutside(range))
5d7836c4 2851 {
603f702b
JS
2852 // Deletes the content of this object within the given range
2853 obj->DeleteRange(range);
99404ab0 2854
603f702b
JS
2855 wxRichTextRange thisRange = obj->GetRange();
2856 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2857
603f702b
JS
2858 // If the whole paragraph is within the range to delete,
2859 // delete the whole thing.
2860 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2861 {
603f702b
JS
2862 // Delete the whole object
2863 RemoveChild(obj, true);
2864 obj = NULL;
99404ab0 2865 }
603f702b
JS
2866 else if (!firstPara)
2867 firstPara = obj;
5d7836c4 2868
603f702b
JS
2869 // If the range includes the paragraph end, we need to join this
2870 // and the next paragraph.
2871 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2872 {
603f702b
JS
2873 // We need to move the objects from the next paragraph
2874 // to this paragraph
2875
2876 wxRichTextParagraph* nextParagraph = NULL;
2877 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2878 nextParagraph = obj;
6c0ea513 2879 else
603f702b
JS
2880 {
2881 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2882 if (next)
2883 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2884 }
5d7836c4 2885
603f702b
JS
2886 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2887
2888 wxRichTextAttr nextParaAttr;
2889 if (applyFinalParagraphStyle)
2890 {
2891 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2892 // not the next one.
2893 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2894 nextParaAttr = thisAttr;
2895 else
2896 nextParaAttr = nextParagraph->GetAttributes();
2897 }
5d7836c4 2898
603f702b 2899 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2900 {
603f702b
JS
2901 // Move the objects to the previous para
2902 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2903
603f702b
JS
2904 while (node1)
2905 {
2906 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2907
603f702b 2908 firstPara->AppendChild(obj1);
5d7836c4 2909
603f702b
JS
2910 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2911 nextParagraph->GetChildren().Erase(node1);
99404ab0 2912
603f702b
JS
2913 node1 = next1;
2914 }
5d7836c4 2915
603f702b
JS
2916 // Delete the paragraph
2917 RemoveChild(nextParagraph, true);
2918 }
fa01bfdd 2919
603f702b
JS
2920 // Avoid empty paragraphs
2921 if (firstPara && firstPara->GetChildren().GetCount() == 0)
2922 {
2923 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2924 firstPara->AppendChild(text);
2925 }
99404ab0 2926
603f702b
JS
2927 if (applyFinalParagraphStyle)
2928 firstPara->SetAttributes(nextParaAttr);
2929
2930 return true;
2931 }
5d7836c4
JS
2932 }
2933 }
7fe8059f 2934
5d7836c4
JS
2935 node = next;
2936 }
7fe8059f 2937
5d7836c4
JS
2938 return true;
2939}
2940
2941/// Get any text in this object for the given range
2942wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
2943{
2944 int lineCount = 0;
2945 wxString text;
2946 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2947 while (node)
2948 {
2949 wxRichTextObject* child = node->GetData();
2950 if (!child->GetRange().IsOutside(range))
2951 {
5d7836c4
JS
2952 wxRichTextRange childRange = range;
2953 childRange.LimitTo(child->GetRange());
7fe8059f 2954
5d7836c4 2955 wxString childText = child->GetTextForRange(childRange);
7fe8059f 2956
5d7836c4
JS
2957 text += childText;
2958
1a75935d 2959 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
2960 text += wxT("\n");
2961
5d7836c4
JS
2962 lineCount ++;
2963 }
2964 node = node->GetNext();
2965 }
2966
2967 return text;
2968}
2969
2970/// Get all the text
2971wxString wxRichTextParagraphLayoutBox::GetText() const
2972{
c99f1b0f 2973 return GetTextForRange(GetOwnRange());
5d7836c4
JS
2974}
2975
2976/// Get the paragraph by number
2977wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
2978{
27e20452 2979 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
2980 return NULL;
2981
2982 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
2983}
2984
2985/// Get the length of the paragraph
2986int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
2987{
2988 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
2989 if (para)
2990 return para->GetRange().GetLength() - 1; // don't include newline
2991 else
2992 return 0;
2993}
2994
2995/// Get the text of the paragraph
2996wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
2997{
2998 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
2999 if (para)
3000 return para->GetTextForRange(para->GetRange());
3001 else
3002 return wxEmptyString;
3003}
3004
3005/// Convert zero-based line column and paragraph number to a position.
3006long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3007{
3008 wxRichTextParagraph* para = GetParagraphAtLine(y);
3009 if (para)
3010 {
3011 return para->GetRange().GetStart() + x;
3012 }
3013 else
3014 return -1;
3015}
3016
3017/// Convert zero-based position to line column and paragraph number
3018bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3019{
3020 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3021 if (para)
3022 {
3023 int count = 0;
3024 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3025 while (node)
3026 {
3027 wxRichTextObject* child = node->GetData();
3028 if (child == para)
3029 break;
3030 count ++;
3031 node = node->GetNext();
3032 }
3033
3034 *y = count;
3035 *x = pos - para->GetRange().GetStart();
3036
3037 return true;
3038 }
3039 else
3040 return false;
3041}
3042
3043/// Get the leaf object in a paragraph at this position.
3044/// Given a line number, get the corresponding wxRichTextLine object.
3045wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3046{
3047 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3048 if (para)
3049 {
3050 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 3051
5d7836c4
JS
3052 while (node)
3053 {
3054 wxRichTextObject* child = node->GetData();
3055 if (child->GetRange().Contains(position))
3056 return child;
7fe8059f 3057
5d7836c4
JS
3058 node = node->GetNext();
3059 }
3060 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3061 return para->GetChildren().GetLast()->GetData();
3062 }
3063 return NULL;
3064}
3065
3066/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 3067bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
3068{
3069 bool characterStyle = false;
3070 bool paragraphStyle = false;
3071
3072 if (style.IsCharacterStyle())
3073 characterStyle = true;
3074 if (style.IsParagraphStyle())
3075 paragraphStyle = true;
3076
603f702b
JS
3077 wxRichTextBuffer* buffer = GetBuffer();
3078
59509217
JS
3079 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3080 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3081 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3082 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 3083 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 3084 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
3085
3086 // Apply paragraph style first, if any
24777478 3087 wxRichTextAttr wholeStyle(style);
523d2f14 3088
603f702b 3089 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 3090 {
603f702b 3091 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 3092 if (def)
603f702b 3093 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 3094 }
59509217
JS
3095
3096 // Limit the attributes to be set to the content to only character attributes.
24777478 3097 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
3098 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3099
603f702b 3100 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 3101 {
603f702b 3102 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 3103 if (def)
603f702b 3104 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
3105 }
3106
5d7836c4
JS
3107 // If we are associated with a control, make undoable; otherwise, apply immediately
3108 // to the data.
3109
603f702b 3110 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3111
3112 wxRichTextAction* action = NULL;
7fe8059f 3113
5d7836c4
JS
3114 if (haveControl && withUndo)
3115 {
603f702b 3116 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3117 action->SetRange(range);
603f702b 3118 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3119 }
3120
3121 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3122 while (node)
3123 {
3124 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3125 // wxASSERT (para != NULL);
5d7836c4
JS
3126
3127 if (para && para->GetChildCount() > 0)
3128 {
3129 // Stop searching if we're beyond the range of interest
3130 if (para->GetRange().GetStart() > range.GetEnd())
3131 break;
3132
3133 if (!para->GetRange().IsOutside(range))
3134 {
3135 // We'll be using a copy of the paragraph to make style changes,
3136 // not updating the buffer directly.
4e09ebe8 3137 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3138
5d7836c4
JS
3139 if (haveControl && withUndo)
3140 {
3141 newPara = new wxRichTextParagraph(*para);
3142 action->GetNewParagraphs().AppendChild(newPara);
3143
3144 // Also store the old ones for Undo
3145 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3146 }
3147 else
3148 newPara = para;
41a85215 3149
a7ed48a5
JS
3150 // If we're specifying paragraphs only, then we really mean character formatting
3151 // to be included in the paragraph style
3152 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3153 {
aeb6ebe2
JS
3154 if (removeStyle)
3155 {
3156 // Removes the given style from the paragraph
3157 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3158 }
3159 else if (resetExistingStyle)
523d2f14
JS
3160 newPara->GetAttributes() = wholeStyle;
3161 else
59509217 3162 {
523d2f14
JS
3163 if (applyMinimal)
3164 {
3165 // Only apply attributes that will make a difference to the combined
3166 // style as seen on the display
603f702b 3167 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3168 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3169 }
3170 else
3171 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3172 }
59509217 3173 }
5d7836c4 3174
5912d19e 3175 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3176 // since they will computed as needed. Only apply the character styling if it's _only_
3177 // character styling. This policy is subject to change and might be put under user control.
3178
59509217
JS
3179 // Hm. we might well be applying a mix of paragraph and character styles, in which
3180 // case we _do_ want to apply character styles regardless of what para styles are set.
3181 // But if we're applying a paragraph style, which has some character attributes, but
3182 // we only want the paragraphs to hold this character style, then we _don't_ want to
3183 // apply the character style. So we need to be able to choose.
3184
f1d800d9 3185 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3186 {
3187 wxRichTextRange childRange(range);
3188 childRange.LimitTo(newPara->GetRange());
7fe8059f 3189
5d7836c4
JS
3190 // Find the starting position and if necessary split it so
3191 // we can start applying a different style.
3192 // TODO: check that the style actually changes or is different
3193 // from style outside of range
4e09ebe8
JS
3194 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3195 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3196
5d7836c4
JS
3197 if (childRange.GetStart() == newPara->GetRange().GetStart())
3198 firstObject = newPara->GetChildren().GetFirst()->GetData();
3199 else
3200 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3201
5d7836c4
JS
3202 // Increment by 1 because we're apply the style one _after_ the split point
3203 long splitPoint = childRange.GetEnd();
3204 if (splitPoint != newPara->GetRange().GetEnd())
3205 splitPoint ++;
7fe8059f 3206
5d7836c4 3207 // Find last object
4b3483e7 3208 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3209 lastObject = newPara->GetChildren().GetLast()->GetData();
3210 else
3211 // lastObject is set as a side-effect of splitting. It's
3212 // returned as the object before the new object.
3213 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3214
5d7836c4
JS
3215 wxASSERT(firstObject != NULL);
3216 wxASSERT(lastObject != NULL);
7fe8059f 3217
5d7836c4
JS
3218 if (!firstObject || !lastObject)
3219 continue;
7fe8059f 3220
5d7836c4
JS
3221 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3222 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3223
4c9847e1
MW
3224 wxASSERT(firstNode);
3225 wxASSERT(lastNode);
7fe8059f 3226
5d7836c4 3227 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3228
5d7836c4
JS
3229 while (node2)
3230 {
3231 wxRichTextObject* child = node2->GetData();
7fe8059f 3232
aeb6ebe2
JS
3233 if (removeStyle)
3234 {
3235 // Removes the given style from the paragraph
3236 wxRichTextRemoveStyle(child->GetAttributes(), style);
3237 }
3238 else if (resetExistingStyle)
523d2f14
JS
3239 child->GetAttributes() = characterAttributes;
3240 else
59509217 3241 {
523d2f14
JS
3242 if (applyMinimal)
3243 {
3244 // Only apply attributes that will make a difference to the combined
3245 // style as seen on the display
603f702b 3246 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3247 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3248 }
3249 else
3250 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3251 }
59509217 3252
5d7836c4
JS
3253 if (node2 == lastNode)
3254 break;
7fe8059f 3255
5d7836c4
JS
3256 node2 = node2->GetNext();
3257 }
3258 }
3259 }
3260 }
3261
3262 node = node->GetNext();
3263 }
3264
3265 // Do action, or delay it until end of batch.
3266 if (haveControl && withUndo)
603f702b 3267 buffer->SubmitAction(action);
5d7836c4
JS
3268
3269 return true;
3270}
3271
603f702b
JS
3272// Just change the attributes for this single object.
3273void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3274{
603f702b 3275 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3276 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3277 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3278 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3279
cdaed652 3280 wxRichTextAction *action = NULL;
603f702b
JS
3281 wxRichTextAttr newAttr = obj->GetAttributes();
3282 if (resetExistingStyle)
3283 newAttr = textAttr;
3284 else
3285 newAttr.Apply(textAttr);
cdaed652
VZ
3286
3287 if (haveControl && withUndo)
3288 {
603f702b
JS
3289 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3290 action->SetRange(obj->GetRange().FromInternal());
3291 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3292 action->MakeObject(obj);
bec80f4f 3293
603f702b 3294 action->GetAttributes() = newAttr;
cdaed652
VZ
3295 }
3296 else
603f702b 3297 obj->GetAttributes() = newAttr;
cdaed652
VZ
3298
3299 if (haveControl && withUndo)
603f702b 3300 buffer->SubmitAction(action);
cdaed652
VZ
3301}
3302
5d7836c4 3303/// Get the text attributes for this position.
24777478 3304bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3305{
fe5aa22c
JS
3306 return DoGetStyle(position, style, true);
3307}
e191ee87 3308
24777478 3309bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3310{
3311 return DoGetStyle(position, style, false);
3312}
3313
fe5aa22c
JS
3314/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3315/// context attributes.
24777478 3316bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3317{
4e09ebe8 3318 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3319
5d7836c4 3320 if (style.IsParagraphStyle())
fe5aa22c 3321 {
5d7836c4 3322 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3323 if (obj)
3324 {
fe5aa22c
JS
3325 if (combineStyles)
3326 {
3327 // Start with the base style
3328 style = GetAttributes();
e191ee87 3329
fe5aa22c
JS
3330 // Apply the paragraph style
3331 wxRichTextApplyStyle(style, obj->GetAttributes());
3332 }
3333 else
3334 style = obj->GetAttributes();
5912d19e 3335
fe5aa22c
JS
3336 return true;
3337 }
5d7836c4
JS
3338 }
3339 else
fe5aa22c
JS
3340 {
3341 obj = GetLeafObjectAtPosition(position);
3342 if (obj)
3343 {
fe5aa22c
JS
3344 if (combineStyles)
3345 {
3346 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3347 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3348 }
3349 else
3350 style = obj->GetAttributes();
5912d19e 3351
fe5aa22c
JS
3352 return true;
3353 }
fe5aa22c
JS
3354 }
3355 return false;
5d7836c4
JS
3356}
3357
59509217
JS
3358static bool wxHasStyle(long flags, long style)
3359{
3360 return (flags & style) != 0;
3361}
3362
3363/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3364/// content.
24777478
JS
3365bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3366{
3367 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3368
3369 return true;
3370}
3371
3372/// Get the combined style for a range - if any attribute is different within the range,
3373/// that attribute is not present within the flags.
3374/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3375/// nested.
3376bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3377{
24777478
JS
3378 style = wxRichTextAttr();
3379
3380 wxRichTextAttr clashingAttr;
3381 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3382
24777478
JS
3383 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3384 while (node)
59509217 3385 {
603f702b
JS
3386 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3387 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3388 {
24777478 3389 if (para->GetChildren().GetCount() == 0)
59509217 3390 {
603f702b 3391 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3392
24777478 3393 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
59509217
JS
3394 }
3395 else
3396 {
24777478
JS
3397 wxRichTextRange paraRange(para->GetRange());
3398 paraRange.LimitTo(range);
59509217 3399
24777478
JS
3400 // First collect paragraph attributes only
3401 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3402 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3403 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
9c4cb611 3404
24777478
JS
3405 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3406
3407 while (childNode)
59509217 3408 {
24777478
JS
3409 wxRichTextObject* child = childNode->GetData();
3410 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3411 {
603f702b 3412 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3413
24777478
JS
3414 // Now collect character attributes only
3415 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3416
24777478
JS
3417 CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
3418 }
59509217 3419
24777478 3420 childNode = childNode->GetNext();
59509217
JS
3421 }
3422 }
59509217 3423 }
24777478 3424 node = node->GetNext();
59509217 3425 }
24777478
JS
3426 return true;
3427}
59509217 3428
24777478
JS
3429/// Set default style
3430bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3431{
3432 m_defaultAttributes = style;
3433 return true;
3434}
59509217 3435
24777478
JS
3436/// Test if this whole range has character attributes of the specified kind. If any
3437/// of the attributes are different within the range, the test fails. You
3438/// can use this to implement, for example, bold button updating. style must have
3439/// flags indicating which attributes are of interest.
3440bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3441{
3442 int foundCount = 0;
3443 int matchingCount = 0;
59509217 3444
24777478
JS
3445 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3446 while (node)
59509217 3447 {
24777478 3448 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3449 // wxASSERT (para != NULL);
59509217 3450
24777478 3451 if (para)
59509217 3452 {
24777478
JS
3453 // Stop searching if we're beyond the range of interest
3454 if (para->GetRange().GetStart() > range.GetEnd())
3455 return foundCount == matchingCount && foundCount != 0;
59509217 3456
24777478 3457 if (!para->GetRange().IsOutside(range))
59509217 3458 {
24777478 3459 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3460
24777478
JS
3461 while (node2)
3462 {
3463 wxRichTextObject* child = node2->GetData();
3464 // Allow for empty string if no buffer
3465 wxRichTextRange childRange = child->GetRange();
3466 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3467 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3468
24777478
JS
3469 if (!childRange.IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
3470 {
3471 foundCount ++;
3472 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3473
24777478
JS
3474 if (wxTextAttrEqPartial(textAttr, style))
3475 matchingCount ++;
3476 }
59509217 3477
24777478
JS
3478 node2 = node2->GetNext();
3479 }
59509217
JS
3480 }
3481 }
59509217 3482
24777478 3483 node = node->GetNext();
59509217
JS
3484 }
3485
24777478
JS
3486 return foundCount == matchingCount && foundCount != 0;
3487}
59509217 3488
24777478
JS
3489/// Test if this whole range has paragraph attributes of the specified kind. If any
3490/// of the attributes are different within the range, the test fails. You
3491/// can use this to implement, for example, centering button updating. style must have
3492/// flags indicating which attributes are of interest.
3493bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3494{
3495 int foundCount = 0;
3496 int matchingCount = 0;
3497
3498 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3499 while (node)
38f833b1 3500 {
24777478 3501 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3502 // wxASSERT (para != NULL);
24777478
JS
3503
3504 if (para)
38f833b1 3505 {
24777478
JS
3506 // Stop searching if we're beyond the range of interest
3507 if (para->GetRange().GetStart() > range.GetEnd())
3508 return foundCount == matchingCount && foundCount != 0;
3509
3510 if (!para->GetRange().IsOutside(range))
38f833b1 3511 {
24777478
JS
3512 wxRichTextAttr textAttr = GetAttributes();
3513 // Apply the paragraph style
3514 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3515
3516 foundCount ++;
3517 if (wxTextAttrEqPartial(textAttr, style))
3518 matchingCount ++;
38f833b1
JS
3519 }
3520 }
24777478
JS
3521
3522 node = node->GetNext();
38f833b1 3523 }
24777478
JS
3524 return foundCount == matchingCount && foundCount != 0;
3525}
5d7836c4 3526
cc2aecde
JS
3527void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3528{
3529 wxRichTextBuffer* buffer = GetBuffer();
3530 if (buffer && buffer->GetRichTextCtrl())
3531 buffer->GetRichTextCtrl()->PrepareContent(container);
3532}
3533
3534
590a0f8b
JS
3535/// Set character or paragraph properties
3536bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3537{
3538 wxRichTextBuffer* buffer = GetBuffer();
3539
3540 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3541 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3542 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3543 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3544 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3545
3546 // If we are associated with a control, make undoable; otherwise, apply immediately
3547 // to the data.
3548
3549 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3550
3551 wxRichTextAction* action = NULL;
3552
3553 if (haveControl && withUndo)
3554 {
3555 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3556 action->SetRange(range);
3557 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3558 }
3559
3560 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3561 while (node)
3562 {
3563 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3564 // wxASSERT (para != NULL);
3565
3566 if (para && para->GetChildCount() > 0)
3567 {
3568 // Stop searching if we're beyond the range of interest
3569 if (para->GetRange().GetStart() > range.GetEnd())
3570 break;
3571
3572 if (!para->GetRange().IsOutside(range))
3573 {
3574 // We'll be using a copy of the paragraph to make style changes,
3575 // not updating the buffer directly.
3576 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3577
3578 if (haveControl && withUndo)
3579 {
3580 newPara = new wxRichTextParagraph(*para);
3581 action->GetNewParagraphs().AppendChild(newPara);
3582
3583 // Also store the old ones for Undo
3584 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3585 }
3586 else
3587 newPara = para;
3588
3589 if (parasOnly)
3590 {
3591 if (removeProperties)
3592 {
3593 // Removes the given style from the paragraph
3594 // TODO
3595 newPara->GetProperties().RemoveProperties(properties);
3596 }
3597 else if (resetExistingProperties)
3598 newPara->GetProperties() = properties;
3599 else
3600 newPara->GetProperties().MergeProperties(properties);
3601 }
3602
3603 // When applying paragraph styles dynamically, don't change the text objects' attributes
3604 // since they will computed as needed. Only apply the character styling if it's _only_
3605 // character styling. This policy is subject to change and might be put under user control.
3606
3607 // Hm. we might well be applying a mix of paragraph and character styles, in which
3608 // case we _do_ want to apply character styles regardless of what para styles are set.
3609 // But if we're applying a paragraph style, which has some character attributes, but
3610 // we only want the paragraphs to hold this character style, then we _don't_ want to
3611 // apply the character style. So we need to be able to choose.
3612
3613 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3614 {
3615 wxRichTextRange childRange(range);
3616 childRange.LimitTo(newPara->GetRange());
3617
3618 // Find the starting position and if necessary split it so
3619 // we can start applying different properties.
3620 // TODO: check that the properties actually change or are different
3621 // from properties outside of range
3622 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3623 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3624
3625 if (childRange.GetStart() == newPara->GetRange().GetStart())
3626 firstObject = newPara->GetChildren().GetFirst()->GetData();
3627 else
3628 firstObject = newPara->SplitAt(range.GetStart());
3629
3630 // Increment by 1 because we're apply the style one _after_ the split point
3631 long splitPoint = childRange.GetEnd();
3632 if (splitPoint != newPara->GetRange().GetEnd())
3633 splitPoint ++;
3634
3635 // Find last object
3636 if (splitPoint == newPara->GetRange().GetEnd())
3637 lastObject = newPara->GetChildren().GetLast()->GetData();
3638 else
3639 // lastObject is set as a side-effect of splitting. It's
3640 // returned as the object before the new object.
3641 (void) newPara->SplitAt(splitPoint, & lastObject);
3642
3643 wxASSERT(firstObject != NULL);
3644 wxASSERT(lastObject != NULL);
3645
3646 if (!firstObject || !lastObject)
3647 continue;
3648
3649 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3650 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3651
3652 wxASSERT(firstNode);
3653 wxASSERT(lastNode);
3654
3655 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3656
3657 while (node2)
3658 {
3659 wxRichTextObject* child = node2->GetData();
3660
3661 if (removeProperties)
3662 {
3663 // Removes the given properties from the paragraph
3664 child->GetProperties().RemoveProperties(properties);
3665 }
3666 else if (resetExistingProperties)
3667 child->GetProperties() = properties;
3668 else
3669 {
3670 child->GetProperties().MergeProperties(properties);
3671 }
3672
3673 if (node2 == lastNode)
3674 break;
3675
3676 node2 = node2->GetNext();
3677 }
3678 }
3679 }
3680 }
3681
3682 node = node->GetNext();
3683 }
3684
3685 // Do action, or delay it until end of batch.
3686 if (haveControl && withUndo)
3687 buffer->SubmitAction(action);
3688
3689 return true;
3690}
3691
5d7836c4
JS
3692void wxRichTextParagraphLayoutBox::Reset()
3693{
3694 Clear();
3695
603f702b
JS
3696 wxRichTextBuffer* buffer = GetBuffer();
3697 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3698 {
603f702b
JS
3699 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3700 event.SetEventObject(buffer->GetRichTextCtrl());
3701 event.SetContainer(this);
cd8ba0d9
JS
3702
3703 buffer->SendEvent(event, true);
3704 }
3705
7fe8059f 3706 AddParagraph(wxEmptyString);
3e541562 3707
cc2aecde
JS
3708 PrepareContent(*this);
3709
603f702b 3710 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3711}
3712
38113684
JS
3713/// Invalidate the buffer. With no argument, invalidates whole buffer.
3714void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3715{
603f702b 3716 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3717
603f702b
JS
3718 DoInvalidate(invalidRange);
3719}
3720
3721// Do the (in)validation for this object only
3722void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3723{
1e967276 3724 if (invalidRange == wxRICHTEXT_ALL)
38113684 3725 {
1e967276 3726 m_invalidRange = wxRICHTEXT_ALL;
38113684 3727 }
1e967276 3728 // Already invalidating everything
603f702b
JS
3729 else if (m_invalidRange == wxRICHTEXT_ALL)
3730 {
3731 }
3732 else
3733 {
3734 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3735 m_invalidRange.SetStart(invalidRange.GetStart());
3736 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3737 m_invalidRange.SetEnd(invalidRange.GetEnd());
3738 }
3739}
39a1c2f2 3740
603f702b
JS
3741// Do the (in)validation both up and down the hierarchy
3742void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3743{
3744 Invalidate(invalidRange);
3745
3746 if (invalidRange != wxRICHTEXT_NONE)
3747 {
3748 // Now go up the hierarchy
3749 wxRichTextObject* thisObj = this;
3750 wxRichTextObject* p = GetParent();
3751 while (p)
3752 {
3753 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3754 if (l)
3755 l->DoInvalidate(thisObj->GetRange());
3756
3757 thisObj = p;
3758 p = p->GetParent();
3759 }
3760 }
38113684
JS
3761}
3762
3763/// Get invalid range, rounding to entire paragraphs if argument is true.
3764wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3765{
1e967276 3766 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3767 return m_invalidRange;
39a1c2f2 3768
38113684 3769 wxRichTextRange range = m_invalidRange;
39a1c2f2 3770
38113684
JS
3771 if (wholeParagraphs)
3772 {
3773 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3774 if (para1)
3775 range.SetStart(para1->GetRange().GetStart());
cdaed652 3776 // floating layout make all child should be relayout
603f702b 3777 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3778 }
3779 return range;
3780}
3781
fe5aa22c
JS
3782/// Apply the style sheet to the buffer, for example if the styles have changed.
3783bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3784{
3785 wxASSERT(styleSheet != NULL);
3786 if (!styleSheet)
3787 return false;
3788
3789 int foundCount = 0;
3790
44580804
JS
3791 wxRichTextAttr attr(GetBasicStyle());
3792 if (GetBasicStyle().HasParagraphStyleName())
3793 {
3794 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3795 if (paraDef)
3796 {
3797 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3798 SetBasicStyle(attr);
3799 foundCount ++;
3800 }
3801 }
3802
3803 if (GetBasicStyle().HasCharacterStyleName())
3804 {
3805 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3806 if (charDef)
3807 {
3808 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3809 SetBasicStyle(attr);
3810 foundCount ++;
3811 }
3812 }
3813
fe5aa22c
JS
3814 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3815 while (node)
3816 {
3817 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3818 // wxASSERT (para != NULL);
fe5aa22c
JS
3819
3820 if (para)
3821 {
38f833b1
JS
3822 // Combine paragraph and list styles. If there is a list style in the original attributes,
3823 // the current indentation overrides anything else and is used to find the item indentation.
3824 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3825 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3826 // exception as above).
3827 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3828 // So when changing a list style interactively, could retrieve level based on current style, then
3829 // set appropriate indent and apply new style.
41a85215 3830
bbd55ff9
JS
3831 int outline = -1;
3832 int num = -1;
3833 if (para->GetAttributes().HasOutlineLevel())
3834 outline = para->GetAttributes().GetOutlineLevel();
3835 if (para->GetAttributes().HasBulletNumber())
3836 num = para->GetAttributes().GetBulletNumber();
3837
38f833b1
JS
3838 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3839 {
3840 int currentIndent = para->GetAttributes().GetLeftIndent();
3841
3842 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3843 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3844 if (paraDef && !listDef)
3845 {
336d8ae9 3846 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3847 foundCount ++;
3848 }
3849 else if (listDef && !paraDef)
3850 {
3851 // Set overall style defined for the list style definition
336d8ae9 3852 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3853
3854 // Apply the style for this level
3855 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3856 foundCount ++;
3857 }
3858 else if (listDef && paraDef)
3859 {
3860 // Combines overall list style, style for level, and paragraph style
336d8ae9 3861 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3862 foundCount ++;
3863 }
3864 }
3865 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3866 {
3867 int currentIndent = para->GetAttributes().GetLeftIndent();
3868
3869 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3870
41a85215 3871 // Overall list definition style
336d8ae9 3872 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3873
38f833b1
JS
3874 // Style for this level
3875 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3876
3877 foundCount ++;
3878 }
3879 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3880 {
3881 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3882 if (def)
3883 {
336d8ae9 3884 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3885 foundCount ++;
3886 }
3887 }
bbd55ff9
JS
3888
3889 if (outline != -1)
3890 para->GetAttributes().SetOutlineLevel(outline);
3891 if (num != -1)
3892 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3893 }
3894
3895 node = node->GetNext();
3896 }
3897 return foundCount != 0;
3898}
3899
38f833b1
JS
3900/// Set list style
3901bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3902{
603f702b
JS
3903 wxRichTextBuffer* buffer = GetBuffer();
3904 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 3905
38f833b1
JS
3906 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3907 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3908 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3909 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 3910
38f833b1
JS
3911 // Current number, if numbering
3912 int n = startFrom;
41a85215 3913
38f833b1
JS
3914 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3915
3916 // If we are associated with a control, make undoable; otherwise, apply immediately
3917 // to the data.
3918
603f702b 3919 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
3920
3921 wxRichTextAction* action = NULL;
3922
3923 if (haveControl && withUndo)
3924 {
603f702b 3925 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 3926 action->SetRange(range);
603f702b 3927 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
3928 }
3929
3930 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3931 while (node)
3932 {
3933 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3934 // wxASSERT (para != NULL);
38f833b1
JS
3935
3936 if (para && para->GetChildCount() > 0)
3937 {
3938 // Stop searching if we're beyond the range of interest
3939 if (para->GetRange().GetStart() > range.GetEnd())
3940 break;
3941
3942 if (!para->GetRange().IsOutside(range))
3943 {
3944 // We'll be using a copy of the paragraph to make style changes,
3945 // not updating the buffer directly.
3946 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3947
3948 if (haveControl && withUndo)
3949 {
3950 newPara = new wxRichTextParagraph(*para);
3951 action->GetNewParagraphs().AppendChild(newPara);
3952
3953 // Also store the old ones for Undo
3954 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3955 }
3956 else
3957 newPara = para;
41a85215 3958
38f833b1
JS
3959 if (def)
3960 {
3961 int thisIndent = newPara->GetAttributes().GetLeftIndent();
3962 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 3963
38f833b1
JS
3964 // How is numbering going to work?
3965 // If we are renumbering, or numbering for the first time, we need to keep
3966 // track of the number for each level. But we might be simply applying a different
3967 // list style.
3968 // In Word, applying a style to several paragraphs, even if at different levels,
3969 // reverts the level back to the same one. So we could do the same here.
3970 // Renumbering will need to be done when we promote/demote a paragraph.
3971
3972 // Apply the overall list style, and item style for this level
24777478 3973 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 3974 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 3975
d2d0adc7 3976 // Now we need to do numbering
38f833b1
JS
3977 if (renumber)
3978 {
3979 newPara->GetAttributes().SetBulletNumber(n);
3980 }
41a85215 3981
38f833b1
JS
3982 n ++;
3983 }
3984 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
3985 {
3986 // if def is NULL, remove list style, applying any associated paragraph style
3987 // to restore the attributes
3988
3989 newPara->GetAttributes().SetListStyleName(wxEmptyString);
3990 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 3991 newPara->GetAttributes().SetBulletText(wxEmptyString);
41a85215 3992
38f833b1 3993 // Eliminate the main list-related attributes
d2d0adc7 3994 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 3995
38f833b1
JS
3996 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
3997 {
3998 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
3999 if (def)
4000 {
336d8ae9 4001 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
4002 }
4003 }
4004 }
4005 }
4006 }
4007
4008 node = node->GetNext();
4009 }
4010
4011 // Do action, or delay it until end of batch.
4012 if (haveControl && withUndo)
603f702b 4013 buffer->SubmitAction(action);
38f833b1
JS
4014
4015 return true;
4016}
4017
4018bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4019{
603f702b
JS
4020 wxRichTextBuffer* buffer = GetBuffer();
4021 if (buffer && buffer->GetStyleSheet())
38f833b1 4022 {
603f702b 4023 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4024 if (def)
4025 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4026 }
4027 return false;
4028}
4029
4030/// Clear list for given range
4031bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4032{
4033 return SetListStyle(range, NULL, flags);
4034}
4035
4036/// Number/renumber any list elements in the given range
4037bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4038{
4039 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4040}
4041
4042/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4043bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4044 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4045{
603f702b
JS
4046 wxRichTextBuffer* buffer = GetBuffer();
4047 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4048
38f833b1
JS
4049 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4050 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 4051#if wxDEBUG_LEVEL
38f833b1 4052 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 4053#endif
38f833b1
JS
4054
4055 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4056
38f833b1
JS
4057 // Max number of levels
4058 const int maxLevels = 10;
41a85215 4059
38f833b1
JS
4060 // The level we're looking at now
4061 int currentLevel = -1;
41a85215 4062
38f833b1
JS
4063 // The item number for each level
4064 int levels[maxLevels];
4065 int i;
41a85215 4066
38f833b1
JS
4067 // Reset all numbering
4068 for (i = 0; i < maxLevels; i++)
4069 {
4070 if (startFrom != -1)
d2d0adc7 4071 levels[i] = startFrom-1;
38f833b1 4072 else if (renumber) // start again
d2d0adc7 4073 levels[i] = 0;
38f833b1
JS
4074 else
4075 levels[i] = -1; // start from the number we found, if any
4076 }
41a85215 4077
bb7bbd12 4078#if wxDEBUG_LEVEL
38f833b1 4079 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
bb7bbd12 4080#endif
38f833b1
JS
4081
4082 // If we are associated with a control, make undoable; otherwise, apply immediately
4083 // to the data.
4084
603f702b 4085 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4086
4087 wxRichTextAction* action = NULL;
4088
4089 if (haveControl && withUndo)
4090 {
603f702b 4091 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4092 action->SetRange(range);
603f702b 4093 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4094 }
4095
4096 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4097 while (node)
4098 {
4099 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4100 // wxASSERT (para != NULL);
38f833b1
JS
4101
4102 if (para && para->GetChildCount() > 0)
4103 {
4104 // Stop searching if we're beyond the range of interest
4105 if (para->GetRange().GetStart() > range.GetEnd())
4106 break;
4107
4108 if (!para->GetRange().IsOutside(range))
4109 {
4110 // We'll be using a copy of the paragraph to make style changes,
4111 // not updating the buffer directly.
4112 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4113
4114 if (haveControl && withUndo)
4115 {
4116 newPara = new wxRichTextParagraph(*para);
4117 action->GetNewParagraphs().AppendChild(newPara);
4118
4119 // Also store the old ones for Undo
4120 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4121 }
4122 else
4123 newPara = para;
41a85215 4124
38f833b1
JS
4125 wxRichTextListStyleDefinition* defToUse = def;
4126 if (!defToUse)
4127 {
336d8ae9
VZ
4128 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4129 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 4130 }
41a85215 4131
38f833b1
JS
4132 if (defToUse)
4133 {
4134 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4135 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4136
d2d0adc7
JS
4137 // If we've specified a level to apply to all, change the level.
4138 if (specifiedLevel != -1)
38f833b1 4139 thisLevel = specifiedLevel;
41a85215 4140
38f833b1
JS
4141 // Do promotion if specified
4142 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4143 {
4144 thisLevel = thisLevel - promoteBy;
4145 if (thisLevel < 0)
4146 thisLevel = 0;
4147 if (thisLevel > 9)
4148 thisLevel = 9;
4149 }
41a85215 4150
38f833b1 4151 // Apply the overall list style, and item style for this level
24777478 4152 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4153 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4154
38f833b1 4155 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 4156
38f833b1
JS
4157 if (currentLevel == -1)
4158 currentLevel = thisLevel;
41a85215 4159
38f833b1
JS
4160 // Same level as before, do nothing except increment level's number afterwards
4161 if (currentLevel == thisLevel)
4162 {
4163 }
4164 // A deeper level: start renumbering all levels after current level
4165 else if (thisLevel > currentLevel)
4166 {
4167 for (i = currentLevel+1; i <= thisLevel; i++)
4168 {
d2d0adc7 4169 levels[i] = 0;
38f833b1
JS
4170 }
4171 currentLevel = thisLevel;
4172 }
4173 else if (thisLevel < currentLevel)
4174 {
4175 currentLevel = thisLevel;
41a85215 4176 }
38f833b1
JS
4177
4178 // Use the current numbering if -1 and we have a bullet number already
4179 if (levels[currentLevel] == -1)
4180 {
4181 if (newPara->GetAttributes().HasBulletNumber())
4182 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4183 else
4184 levels[currentLevel] = 1;
4185 }
d2d0adc7
JS
4186 else
4187 {
4188 levels[currentLevel] ++;
4189 }
41a85215 4190
38f833b1
JS
4191 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4192
d2d0adc7
JS
4193 // Create the bullet text if an outline list
4194 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4195 {
4196 wxString text;
4197 for (i = 0; i <= currentLevel; i++)
4198 {
4199 if (!text.IsEmpty())
4200 text += wxT(".");
4201 text += wxString::Format(wxT("%d"), levels[i]);
4202 }
4203 newPara->GetAttributes().SetBulletText(text);
4204 }
38f833b1
JS
4205 }
4206 }
4207 }
4208
4209 node = node->GetNext();
4210 }
4211
4212 // Do action, or delay it until end of batch.
4213 if (haveControl && withUndo)
603f702b 4214 buffer->SubmitAction(action);
38f833b1
JS
4215
4216 return true;
4217}
4218
4219bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4220{
603f702b
JS
4221 wxRichTextBuffer* buffer = GetBuffer();
4222 if (buffer->GetStyleSheet())
38f833b1
JS
4223 {
4224 wxRichTextListStyleDefinition* def = NULL;
4225 if (!defName.IsEmpty())
603f702b 4226 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4227 return NumberList(range, def, flags, startFrom, specifiedLevel);
4228 }
4229 return false;
4230}
4231
4232/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4233bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4234{
4235 // TODO
4236 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4237 // to NumberList with a flag indicating promotion is required within one of the ranges.
4238 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4239 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4240 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4241 // list position will start from 1.
4242 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4243 // We can end the renumbering at this point.
41a85215 4244
38f833b1 4245 // For now, only renumber within the promotion range.
41a85215 4246
38f833b1
JS
4247 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4248}
4249
4250bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4251{
603f702b
JS
4252 wxRichTextBuffer* buffer = GetBuffer();
4253 if (buffer->GetStyleSheet())
38f833b1
JS
4254 {
4255 wxRichTextListStyleDefinition* def = NULL;
4256 if (!defName.IsEmpty())
603f702b 4257 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4258 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4259 }
4260 return false;
4261}
4262
d2d0adc7
JS
4263/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4264/// position of the paragraph that it had to start looking from.
24777478 4265bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 4266{
d2d0adc7
JS
4267 if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4268 return false;
3e541562 4269
603f702b
JS
4270 wxRichTextBuffer* buffer = GetBuffer();
4271 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4272 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 4273 {
336d8ae9 4274 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
4275 if (def)
4276 {
4277 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4278 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4279
d2d0adc7
JS
4280 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4281
4282 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4283 if (previousParagraph->GetAttributes().HasBulletName())
4284 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4285 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4286 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4287
d2d0adc7
JS
4288 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4289 attr.SetBulletNumber(nextNumber);
3e541562 4290
d2d0adc7
JS
4291 if (isOutline)
4292 {
4293 wxString text = previousParagraph->GetAttributes().GetBulletText();
4294 if (!text.IsEmpty())
4295 {
4296 int pos = text.Find(wxT('.'), true);
4297 if (pos != wxNOT_FOUND)
4298 {
4299 text = text.Mid(0, text.Length() - pos - 1);
4300 }
4301 else
4302 text = wxEmptyString;
4303 if (!text.IsEmpty())
4304 text += wxT(".");
4305 text += wxString::Format(wxT("%d"), nextNumber);
4306 attr.SetBulletText(text);
4307 }
4308 }
3e541562 4309
d2d0adc7
JS
4310 return true;
4311 }
4312 else
4313 return false;
4314 }
4315 else
4316 return false;
4317}
4318
5d7836c4
JS
4319/*!
4320 * wxRichTextParagraph
4321 * This object represents a single paragraph (or in a straight text editor, a line).
4322 */
4323
603f702b 4324IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4325
cfa3b256
JS
4326wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4327
24777478 4328wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4329 wxRichTextCompositeObject(parent)
5d7836c4 4330{
5d7836c4
JS
4331 if (style)
4332 SetAttributes(*style);
4333}
4334
24777478 4335wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4336 wxRichTextCompositeObject(parent)
5d7836c4 4337{
4f32b3cf
JS
4338 if (paraStyle)
4339 SetAttributes(*paraStyle);
5d7836c4 4340
4f32b3cf 4341 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4342}
4343
4344wxRichTextParagraph::~wxRichTextParagraph()
4345{
4346 ClearLines();
4347}
4348
4349/// Draw the item
8db2e3ef 4350bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4351{
603f702b
JS
4352 if (!IsShown())
4353 return true;
4354
4355 // Currently we don't merge these attributes with the parent, but we
4356 // should consider whether we should (e.g. if we set a border colour
4357 // for all paragraphs). But generally box attributes are likely to be
4358 // different for different objects.
4359 wxRect paraRect = GetRect();
24777478 4360 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef
JS
4361 context.ApplyVirtualAttributes(attr, this);
4362
4363 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
fe5aa22c 4364
5d7836c4 4365 // Draw the bullet, if any
fe5aa22c 4366 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4 4367 {
fe5aa22c 4368 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4369 {
fe5aa22c 4370 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4371 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4372
8db2e3ef 4373 wxRichTextAttr bulletAttr(attr);
d2d0adc7 4374
e3eac0ff
JS
4375 // Combine with the font of the first piece of content, if one is specified
4376 if (GetChildren().GetCount() > 0)
4377 {
4378 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4379 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4380 {
4381 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4382 }
4383 }
4384
d2d0adc7 4385 // Get line height from first line, if any
d3b9f782 4386 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4387
4388 wxPoint linePos;
4389 int lineHeight wxDUMMY_INITIALIZE(0);
4390 if (line)
5d7836c4 4391 {
d2d0adc7
JS
4392 lineHeight = line->GetSize().y;
4393 linePos = line->GetPosition() + GetPosition();
5d7836c4 4394 }
d2d0adc7 4395 else
f089713f 4396 {
f089713f 4397 wxFont font;
44cc96a8
JS
4398 if (bulletAttr.HasFont() && GetBuffer())
4399 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4400 else
4401 font = (*wxNORMAL_FONT);
4402
ecb5fbf1 4403 wxCheckSetFont(dc, font);
f089713f 4404
d2d0adc7
JS
4405 lineHeight = dc.GetCharHeight();
4406 linePos = GetPosition();
4407 linePos.y += spaceBeforePara;
4408 }
f089713f 4409
d2d0adc7 4410 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4411
d2d0adc7
JS
4412 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4413 {
4414 if (wxRichTextBuffer::GetRenderer())
4415 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4416 }
3e541562
JS
4417 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4418 {
d2d0adc7
JS
4419 if (wxRichTextBuffer::GetRenderer())
4420 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4421 }
5d7836c4
JS
4422 else
4423 {
4424 wxString bulletText = GetBulletText();
3e541562 4425
d2d0adc7
JS
4426 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4427 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4428 }
4429 }
4430 }
7fe8059f 4431
5d7836c4
JS
4432 // Draw the range for each line, one object at a time.
4433
4434 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4435 while (node)
4436 {
4437 wxRichTextLine* line = node->GetData();
1e967276 4438 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4439
5d7836c4
JS
4440 // Lines are specified relative to the paragraph
4441
4442 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4443
7051fa41
JS
4444 // Don't draw if off the screen
4445 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4446 {
7051fa41
JS
4447 wxPoint objectPosition = linePosition;
4448 int maxDescent = line->GetDescent();
4449
4450 // Loop through objects until we get to the one within range
4451 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4452
7051fa41
JS
4453 int i = 0;
4454 while (node2)
5d7836c4 4455 {
7051fa41 4456 wxRichTextObject* child = node2->GetData();
5d7836c4 4457
cdaed652 4458 if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4459 {
7051fa41
JS
4460 // Draw this part of the line at the correct position
4461 wxRichTextRange objectRange(child->GetRange());
4462 objectRange.LimitTo(lineRange);
4463
4464 wxSize objectSize;
603f702b 4465 if (child->IsTopLevel())
7051fa41 4466 {
603f702b
JS
4467 objectSize = child->GetCachedSize();
4468 objectRange = child->GetOwnRange();
7051fa41
JS
4469 }
4470 else
7051fa41 4471 {
603f702b
JS
4472#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4473 if (i < (int) line->GetObjectSizes().GetCount())
4474 {
4475 objectSize.x = line->GetObjectSizes()[(size_t) i];
4476 }
4477 else
4478#endif
4479 {
4480 int descent = 0;
8db2e3ef 4481 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
603f702b 4482 }
7051fa41 4483 }
5d7836c4 4484
7051fa41
JS
4485 // Use the child object's width, but the whole line's height
4486 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
8db2e3ef 4487 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4488
7051fa41
JS
4489 objectPosition.x += objectSize.x;
4490 i ++;
4491 }
4492 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4493 // Can break out of inner loop now since we've passed this line's range
4494 break;
5d7836c4 4495
7051fa41
JS
4496 node2 = node2->GetNext();
4497 }
5d7836c4
JS
4498 }
4499
4500 node = node->GetNext();
7fe8059f 4501 }
5d7836c4
JS
4502
4503 return true;
4504}
4505
4f3d5bc0
JS
4506// Get the range width using partial extents calculated for the whole paragraph.
4507static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4508{
4509 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4510
affbfa1f
JS
4511 if (partialExtents.GetCount() < (size_t) range.GetLength())
4512 return 0;
4513
4f3d5bc0
JS
4514 int leftMostPos = 0;
4515 if (range.GetStart() - para.GetRange().GetStart() > 0)
4516 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4517
4518 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4519
4520 int w = rightMostPos - leftMostPos;
4521
4522 return w;
4523}
4524
5d7836c4 4525/// Lay the item out
8db2e3ef 4526bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 4527{
cdaed652
VZ
4528 // Deal with floating objects firstly before the normal layout
4529 wxRichTextBuffer* buffer = GetBuffer();
4530 wxASSERT(buffer);
07d4142f 4531 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
cdaed652 4532 wxASSERT(collector);
8db2e3ef 4533 LayoutFloat(dc, context, rect, style, collector);
cdaed652 4534
24777478 4535 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef 4536 context.ApplyVirtualAttributes(attr, this);
fe5aa22c 4537
169adfa9
JS
4538 // ClearLines();
4539
5d7836c4 4540 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4541 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4542 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4543 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4544 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4545 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4546
4547 int lineSpacing = 0;
4548
4549 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
a1b806b9 4550 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
5d7836c4 4551 {
8f0e4366
JS
4552 wxCheckSetFont(dc, attr.GetFont());
4553 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
5d7836c4
JS
4554 }
4555
5d7836c4
JS
4556 // Start position for each line relative to the paragraph
4557 int startPositionFirstLine = leftIndent;
4558 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4559
4560 // If we have a bullet in this paragraph, the start position for the first line's text
4561 // is actually leftIndent + leftSubIndent.
fe5aa22c 4562 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4563 startPositionFirstLine = startPositionSubsequentLines;
4564
5d7836c4
JS
4565 long lastEndPos = GetRange().GetStart()-1;
4566 long lastCompletedEndPos = lastEndPos;
4567
4568 int currentWidth = 0;
4569 SetPosition(rect.GetPosition());
4570
4571 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4572 int lineHeight = 0;
4573 int maxWidth = 0;
603f702b 4574 int maxHeight = currentPosition.y;
476a319a 4575 int maxAscent = 0;
5d7836c4 4576 int maxDescent = 0;
5d7836c4 4577 int lineCount = 0;
cdaed652
VZ
4578 int lineAscent = 0;
4579 int lineDescent = 0;
5d7836c4 4580
2f45f554
JS
4581 wxRichTextObjectList::compatibility_iterator node;
4582
4583#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4584 wxUnusedVar(style);
4585 wxArrayInt partialExtents;
4586
4587 wxSize paraSize;
8aab23a1 4588 int paraDescent = 0;
2f45f554
JS
4589
4590 // This calculates the partial text extents
8db2e3ef 4591 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
2f45f554
JS
4592#else
4593 node = m_children.GetFirst();
ecb5fbf1
JS
4594 while (node)
4595 {
4596 wxRichTextObject* child = node->GetData();
4597
603f702b 4598 //child->SetCachedSize(wxDefaultSize);
8db2e3ef 4599 child->Layout(dc, context, rect, style);
ecb5fbf1
JS
4600
4601 node = node->GetNext();
4602 }
4603
31778480
JS
4604#endif
4605
5d7836c4
JS
4606 // Split up lines
4607
4608 // We may need to go back to a previous child, in which case create the new line,
4609 // find the child corresponding to the start position of the string, and
4610 // continue.
4611
603f702b
JS
4612 wxRect availableRect;
4613
ecb5fbf1 4614 node = m_children.GetFirst();
5d7836c4
JS
4615 while (node)
4616 {
4617 wxRichTextObject* child = node->GetData();
4618
cdaed652 4619 // If floating, ignore. We already laid out floats.
603f702b
JS
4620 // Also ignore if empty object, except if we haven't got any
4621 // size yet.
4622 if (child->IsFloating() || !child->IsShown() ||
4623 (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4624 )
affbfa1f
JS
4625 {
4626 node = node->GetNext();
4627 continue;
4628 }
4629
5d7836c4
JS
4630 // If this is e.g. a composite text box, it will need to be laid out itself.
4631 // But if just a text fragment or image, for example, this will
4632 // do nothing. NB: won't we need to set the position after layout?
4633 // since for example if position is dependent on vertical line size, we
4634 // can't tell the position until the size is determined. So possibly introduce
4635 // another layout phase.
4636
5d7836c4
JS
4637 // We may only be looking at part of a child, if we searched back for wrapping
4638 // and found a suitable point some way into the child. So get the size for the fragment
4639 // if necessary.
3e541562 4640
ff76711f
JS
4641 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4642 long lastPosToUse = child->GetRange().GetEnd();
4643 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4644
ff76711f
JS
4645 if (lineBreakInThisObject)
4646 lastPosToUse = nextBreakPos;
5d7836c4
JS
4647
4648 wxSize childSize;
4649 int childDescent = 0;
3e541562 4650
603f702b
JS
4651 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4652 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4653 rect.width - startOffset - rightIndent, rect.height);
4654
4655 if (child->IsTopLevel())
4656 {
4657 wxSize oldSize = child->GetCachedSize();
4658
4659 child->Invalidate(wxRICHTEXT_ALL);
4660 child->SetPosition(wxPoint(0, 0));
4661
4662 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4663 // lays out the object again using the minimum size
4664 // The position will be determined by its location in its line,
4665 // and not by the child's actual position.
8db2e3ef
JS
4666 child->LayoutToBestSize(dc, context, buffer,
4667 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4668
4669 if (oldSize != child->GetCachedSize())
4670 {
4671 partialExtents.Clear();
4672
4673 // Recalculate the partial text extents since the child object changed size
8db2e3ef 4674 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
603f702b
JS
4675 }
4676 }
4677
4678 // Problem: we need to layout composites here for which we need the available width,
4679 // but we can't get the available width without using the float collector which
4680 // needs to know the object height.
4681
ff76711f 4682 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4683 {
4684 childSize = child->GetCachedSize();
4685 childDescent = child->GetDescent();
4686 }
4687 else
4f3d5bc0
JS
4688 {
4689#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4690 // Get height only, then the width using the partial extents
8db2e3ef 4691 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4f3d5bc0
JS
4692 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4693#else
8db2e3ef 4694 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
4f3d5bc0
JS
4695#endif
4696 }
ff76711f 4697
603f702b
JS
4698 bool doLoop = true;
4699 int loopIterations = 0;
4700
4701 // If there are nested objects that need to lay themselves out, we have to do this in a
4702 // loop because the height of the object may well depend on the available width.
4703 // And because of floating object positioning, the available width depends on the
4704 // height of the object and whether it will clash with the floating objects.
4705 // So, we see whether the available width changes due to the presence of floating images.
4706 // If it does, then we'll use the new restricted width to find the object height again.
4707 // If this causes another restriction in the available width, we'll try again, until
4708 // either we lose patience or the available width settles down.
4709 do
4710 {
4711 loopIterations ++;
4712
4713 wxRect oldAvailableRect = availableRect;
4714
4715 // Available width depends on the floating objects and the line height.
4716 // Note: the floating objects may be placed vertically along the two side of
4717 // buffer, so we may have different available line widths with different
4718 // [startY, endY]. So, we can't determine how wide the available
4719 // space is until we know the exact line height.
4720 lineDescent = wxMax(childDescent, maxDescent);
4721 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4722 lineHeight = lineDescent + lineAscent;
4723 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4724
4725 // Adjust availableRect to the space that is available when taking floating objects into account.
4726
4727 if (floatAvailableRect.x + startOffset > availableRect.x)
4728 {
4729 int newX = floatAvailableRect.x + startOffset;
4730 int newW = availableRect.width - (newX - availableRect.x);
4731 availableRect.x = newX;
4732 availableRect.width = newW;
4733 }
4734
4735 if (floatAvailableRect.width < availableRect.width)
4736 availableRect.width = floatAvailableRect.width;
4737
4738 currentPosition.x = availableRect.x - rect.x;
4739
4740 if (child->IsTopLevel() && loopIterations <= 20)
4741 {
4742 if (availableRect != oldAvailableRect)
4743 {
4744 wxSize oldSize = child->GetCachedSize();
4745
4746 //child->SetCachedSize(wxDefaultSize);
4747 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4748 // lays out the object again using the minimum size
4749 child->Invalidate(wxRICHTEXT_ALL);
8db2e3ef
JS
4750 child->LayoutToBestSize(dc, context, buffer,
4751 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4752 childSize = child->GetCachedSize();
4753 childDescent = child->GetDescent();
4754 //child->SetPosition(availableRect.GetPosition());
4755
4756 if (oldSize != child->GetCachedSize())
4757 {
4758 partialExtents.Clear();
4759
4760 // Recalculate the partial text extents since the child object changed size
8db2e3ef 4761 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
603f702b 4762 }
cdaed652 4763
603f702b
JS
4764 // Go around the loop finding the available rect for the given floating objects
4765 }
4766 else
4767 doLoop = false;
4768 }
4769 else
4770 doLoop = false;
4771 }
4772 while (doLoop);
cdaed652 4773
ff76711f
JS
4774 // Cases:
4775 // 1) There was a line break BEFORE the natural break
4776 // 2) There was a line break AFTER the natural break
603f702b
JS
4777 // 3) It's the last line
4778 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4779
603f702b
JS
4780 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4781 ||
4782 (childSize.x + currentWidth > availableRect.width)
4783 ||
4784 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4785
4786 )
5d7836c4 4787 {
603f702b
JS
4788 if (child->IsTopLevel())
4789 {
4790 // We can move it to the correct position at this point
4791 child->Move(GetPosition() + wxPoint(currentWidth, currentPosition.y));
4792 }
4793
5d7836c4 4794 long wrapPosition = 0;
603f702b
JS
4795 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4796 wrapPosition = child->GetRange().GetEnd();
4797 else
5d7836c4
JS
4798
4799 // Find a place to wrap. This may walk back to previous children,
4800 // for example if a word spans several objects.
cdaed652
VZ
4801 // Note: one object must contains only one wxTextAtrr, so the line height will not
4802 // change inside one object. Thus, we can pass the remain line width to the
4803 // FindWrapPosition function.
8db2e3ef 4804 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4805 {
4806 // If the function failed, just cut it off at the end of this child.
4807 wrapPosition = child->GetRange().GetEnd();
4808 }
4809
4810 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4811 if (wrapPosition <= lastCompletedEndPos)
4812 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4813
603f702b
JS
4814 // Line end position shouldn't be the same as the end, or greater.
4815 if (wrapPosition >= GetRange().GetEnd())
4816 wrapPosition = GetRange().GetEnd()-1;
4817
5d7836c4 4818 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4819
5d7836c4
JS
4820 // Let's find the actual size of the current line now
4821 wxSize actualSize;
4822 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4823
4ab8a5e2
JS
4824 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4825 /// for the fragment we're about to add.
4826 childDescent = maxDescent;
4827
4f3d5bc0 4828#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4829 if (!child->IsEmpty())
4830 {
4831 // Get height only, then the width using the partial extents
8db2e3ef 4832 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
603f702b
JS
4833 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4834 }
4835 else
4f3d5bc0 4836#endif
8db2e3ef 4837 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
4f3d5bc0 4838
5d7836c4 4839 currentWidth = actualSize.x;
5d7836c4 4840 maxDescent = wxMax(childDescent, maxDescent);
476a319a
JS
4841 maxAscent = wxMax(actualSize.y-childDescent, maxAscent);
4842 lineHeight = maxDescent + maxAscent;
7fe8059f 4843
07d4142f 4844 if (lineHeight == 0 && buffer)
603f702b 4845 {
07d4142f 4846 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
4847 wxCheckSetFont(dc, font);
4848 lineHeight = dc.GetCharHeight();
4849 }
4850
4851 if (maxDescent == 0)
4852 {
4853 int w, h;
4854 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
4855 }
4856
5d7836c4 4857 // Add a new line
1e967276 4858 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 4859
1e967276
JS
4860 // Set relative range so we won't have to change line ranges when paragraphs are moved
4861 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
4862 line->SetPosition(currentPosition);
4863 line->SetSize(wxSize(currentWidth, lineHeight));
4864 line->SetDescent(maxDescent);
4865
603f702b
JS
4866 maxHeight = currentPosition.y + lineHeight;
4867
5d7836c4
JS
4868 // Now move down a line. TODO: add margins, spacing
4869 currentPosition.y += lineHeight;
4870 currentPosition.y += lineSpacing;
5d7836c4 4871 maxDescent = 0;
476a319a 4872 maxAscent = 0;
603f702b
JS
4873 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4874 currentWidth = 0;
7fe8059f 4875
5d7836c4
JS
4876 lineCount ++;
4877
4878 // TODO: account for zero-length objects, such as fields
603f702b 4879 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 4880
5d7836c4
JS
4881 lastEndPos = wrapPosition;
4882 lastCompletedEndPos = lastEndPos;
4883
4884 lineHeight = 0;
4885
603f702b
JS
4886 if (wrapPosition < GetRange().GetEnd()-1)
4887 {
4888 // May need to set the node back to a previous one, due to searching back in wrapping
4889 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
4890 if (childAfterWrapPosition)
4891 node = m_children.Find(childAfterWrapPosition);
4892 else
4893 node = node->GetNext();
4894 }
5d7836c4
JS
4895 else
4896 node = node->GetNext();
603f702b
JS
4897
4898 // Apply paragraph styles such as alignment to the wrapped line
4899 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
4900 }
4901 else
4902 {
4903 // We still fit, so don't add a line, and keep going
4904 currentWidth += childSize.x;
5d7836c4 4905 maxDescent = wxMax(childDescent, maxDescent);
476a319a
JS
4906 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
4907 lineHeight = maxDescent + maxAscent;
5d7836c4 4908
603f702b 4909 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
4910 lastEndPos = child->GetRange().GetEnd();
4911
4912 node = node->GetNext();
4913 }
4914 }
4915
07d4142f 4916 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 4917
1e967276
JS
4918 // Remove remaining unused line objects, if any
4919 ClearUnusedLines(lineCount);
4920
603f702b
JS
4921 // We need to add back the margins etc.
4922 {
4923 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4924 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 4925 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4926 SetCachedSize(marginRect.GetSize());
4927 }
4928
4929 // The maximum size is the length of the paragraph stretched out into a line.
4930 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4931 // this size. TODO: take into account line breaks.
4932 {
4933 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 4934 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 4935 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4936 SetMaxSize(marginRect.GetSize());
4937 }
4938
4939 // Find the greatest minimum size. Currently we only look at non-text objects,
4940 // which isn't ideal but it would be slow to find the maximum word width to
4941 // use as the minimum.
4942 {
4943 int minWidth = 0;
4944 node = m_children.GetFirst();
4945 while (node)
4946 {
4947 wxRichTextObject* child = node->GetData();
4948
4949 // If floating, ignore. We already laid out floats.
4950 // Also ignore if empty object, except if we haven't got any
4951 // size yet.
4952 if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
4953 {
4954 if (child->GetCachedSize().x > minWidth)
4955 minWidth = child->GetMinSize().x;
4956 }
4957 node = node->GetNext();
4958 }
5d7836c4 4959
603f702b
JS
4960 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4961 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 4962 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4963 SetMinSize(marginRect.GetSize());
4964 }
5d7836c4 4965
5d7836c4 4966
2f45f554
JS
4967#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4968#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4969 // Use the text extents to calculate the size of each fragment in each line
4970 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
4971 while (lineNode)
4972 {
4973 wxRichTextLine* line = lineNode->GetData();
4974 wxRichTextRange lineRange = line->GetAbsoluteRange();
4975
4976 // Loop through objects until we get to the one within range
4977 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4978
4979 while (node2)
4980 {
4981 wxRichTextObject* child = node2->GetData();
4982
affbfa1f 4983 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
4984 {
4985 wxRichTextRange rangeToUse = lineRange;
4986 rangeToUse.LimitTo(child->GetRange());
4987
4988 // Find the size of the child from the text extents, and store in an array
4989 // for drawing later
4990 int left = 0;
4991 if (rangeToUse.GetStart() > GetRange().GetStart())
4992 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
4993 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
4994 int sz = right - left;
4995 line->GetObjectSizes().Add(sz);
4996 }
4997 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4998 // Can break out of inner loop now since we've passed this line's range
4999 break;
5000
5001 node2 = node2->GetNext();
5002 }
5003
5004 lineNode = lineNode->GetNext();
5005 }
5006#endif
5007#endif
5008
5d7836c4
JS
5009 return true;
5010}
5011
603f702b
JS
5012/// Apply paragraph styles, such as centering, to wrapped lines
5013/// TODO: take into account box attributes, possibly
5014void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5015{
5016 if (!attr.HasAlignment())
5017 return;
5018
5019 wxPoint pos = line->GetPosition();
5020 wxSize size = line->GetSize();
5021
5022 // centering, right-justification
8db2e3ef 5023 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5024 {
5025 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5026 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5027 line->SetPosition(pos);
5028 }
8db2e3ef 5029 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5030 {
5031 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5032 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5033 line->SetPosition(pos);
5034 }
5035}
5d7836c4
JS
5036
5037/// Insert text at the given position
5038bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5039{
5040 wxRichTextObject* childToUse = NULL;
09f14108 5041 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5042
5043 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5044 while (node)
5045 {
5046 wxRichTextObject* child = node->GetData();
5047 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5048 {
5049 childToUse = child;
5050 nodeToUse = node;
5051 break;
5052 }
5053
5054 node = node->GetNext();
5055 }
5056
5057 if (childToUse)
5058 {
5059 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5060 if (textObject)
5061 {
5062 int posInString = pos - textObject->GetRange().GetStart();
5063
5064 wxString newText = textObject->GetText().Mid(0, posInString) +
5065 text + textObject->GetText().Mid(posInString);
5066 textObject->SetText(newText);
5067
28f92d74 5068 int textLength = text.length();
5d7836c4
JS
5069
5070 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5071 textObject->GetRange().GetEnd() + textLength));
5072
5073 // Increment the end range of subsequent fragments in this paragraph.
5074 // We'll set the paragraph range itself at a higher level.
5075
5076 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5077 while (node)
5078 {
5079 wxRichTextObject* child = node->GetData();
5080 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5081 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5082
5d7836c4
JS
5083 node = node->GetNext();
5084 }
5085
5086 return true;
5087 }
5088 else
5089 {
5090 // TODO: if not a text object, insert at closest position, e.g. in front of it
5091 }
5092 }
5093 else
5094 {
5095 // Add at end.
5096 // Don't pass parent initially to suppress auto-setting of parent range.
5097 // We'll do that at a higher level.
5098 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5099
5100 AppendChild(textObject);
5101 return true;
5102 }
5103
5104 return false;
5105}
5106
5107void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5108{
bec80f4f 5109 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5110}
5111
5112/// Clear the cached lines
5113void wxRichTextParagraph::ClearLines()
5114{
5115 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5116}
5117
5118/// Get/set the object size for the given range. Returns false if the range
5119/// is invalid for this object.
8db2e3ef 5120bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
5121{
5122 if (!range.IsWithin(GetRange()))
5123 return false;
5124
5125 if (flags & wxRICHTEXT_UNFORMATTED)
5126 {
5127 // Just use unformatted data, assume no line breaks
5128 // TODO: take into account line breaks
5129
5130 wxSize sz;
5131
31778480
JS
5132 wxArrayInt childExtents;
5133 wxArrayInt* p;
5134 if (partialExtents)
5135 p = & childExtents;
5136 else
5137 p = NULL;
5138
5d7836c4
JS
5139 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5140 while (node)
5141 {
31778480 5142
5d7836c4
JS
5143 wxRichTextObject* child = node->GetData();
5144 if (!child->GetRange().IsOutside(range))
5145 {
cdaed652
VZ
5146 // Floating objects have a zero size within the paragraph.
5147 if (child->IsFloating())
5148 {
5149 if (partialExtents)
5150 {
5151 int lastSize;
5152 if (partialExtents->GetCount() > 0)
5153 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5154 else
5155 lastSize = 0;
5156
5157 partialExtents->Add(0 /* zero size */ + lastSize);
5158 }
5159 }
5160 else
5161 {
603f702b 5162 wxSize childSize;
4f3d5bc0 5163
603f702b
JS
5164 wxRichTextRange rangeToUse = range;
5165 rangeToUse.LimitTo(child->GetRange());
5166#if 0
5167 if (child->IsTopLevel())
5168 rangeToUse = child->GetOwnRange();
5169#endif
5170 int childDescent = 0;
31778480 5171
603f702b
JS
5172 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
5173 // but it's only going to be used after caching has taken place.
5174 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5175 {
603f702b
JS
5176 childDescent = child->GetDescent();
5177 childSize = child->GetCachedSize();
2f45f554 5178
603f702b
JS
5179 sz.y = wxMax(sz.y, childSize.y);
5180 sz.x += childSize.x;
5181 descent = wxMax(descent, childDescent);
5182 }
5183 else if (child->IsTopLevel())
31778480 5184 {
603f702b
JS
5185 childDescent = child->GetDescent();
5186 childSize = child->GetCachedSize();
31778480 5187
603f702b
JS
5188 sz.y = wxMax(sz.y, childSize.y);
5189 sz.x += childSize.x;
5190 descent = wxMax(descent, childDescent);
5191 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5192 {
603f702b
JS
5193 child->SetCachedSize(childSize);
5194 child->SetDescent(childDescent);
31778480 5195 }
31778480 5196
603f702b
JS
5197 if (partialExtents)
5198 {
5199 int lastSize;
5200 if (partialExtents->GetCount() > 0)
5201 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5202 else
5203 lastSize = 0;
5204
5205 partialExtents->Add(childSize.x + lastSize);
5206 }
5207 }
8db2e3ef 5208 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b
JS
5209 {
5210 sz.y = wxMax(sz.y, childSize.y);
5211 sz.x += childSize.x;
5212 descent = wxMax(descent, childDescent);
5213
5214 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5215 {
5216 child->SetCachedSize(childSize);
5217 child->SetDescent(childDescent);
5218 }
5219
5220 if (partialExtents)
5221 {
5222 int lastSize;
5223 if (partialExtents->GetCount() > 0)
5224 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5225 else
5226 lastSize = 0;
5227
5228 size_t i;
5229 for (i = 0; i < childExtents.GetCount(); i++)
5230 {
5231 partialExtents->Add(childExtents[i] + lastSize);
5232 }
5233 }
5234 }
5235 }
5236
5237 if (p)
5238 p->Clear();
5d7836c4
JS
5239 }
5240
5241 node = node->GetNext();
5242 }
5243 size = sz;
5244 }
5245 else
5246 {
5247 // Use formatted data, with line breaks
5248 wxSize sz;
5249
5250 // We're going to loop through each line, and then for each line,
5251 // call GetRangeSize for the fragment that comprises that line.
5252 // Only we have to do that multiple times within the line, because
5253 // the line may be broken into pieces. For now ignore line break commands
5254 // (so we can assume that getting the unformatted size for a fragment
5255 // within a line is the actual size)
5256
5257 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5258 while (node)
5259 {
5260 wxRichTextLine* line = node->GetData();
1e967276
JS
5261 wxRichTextRange lineRange = line->GetAbsoluteRange();
5262 if (!lineRange.IsOutside(range))
5d7836c4
JS
5263 {
5264 wxSize lineSize;
7fe8059f 5265
5d7836c4
JS
5266 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5267 while (node2)
5268 {
5269 wxRichTextObject* child = node2->GetData();
7fe8059f 5270
cdaed652 5271 if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
5d7836c4 5272 {
1e967276 5273 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5274 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5275 if (child->IsTopLevel())
5276 rangeToUse = child->GetOwnRange();
7fe8059f 5277
5d7836c4
JS
5278 wxSize childSize;
5279 int childDescent = 0;
8db2e3ef 5280 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
5d7836c4
JS
5281 {
5282 lineSize.y = wxMax(lineSize.y, childSize.y);
5283 lineSize.x += childSize.x;
5284 }
5285 descent = wxMax(descent, childDescent);
5286 }
7fe8059f 5287
5d7836c4
JS
5288 node2 = node2->GetNext();
5289 }
5290
5291 // Increase size by a line (TODO: paragraph spacing)
5292 sz.y += lineSize.y;
5293 sz.x = wxMax(sz.x, lineSize.x);
5294 }
5295 node = node->GetNext();
5296 }
5297 size = sz;
5298 }
5299 return true;
5300}
5301
5302/// Finds the absolute position and row height for the given character position
8db2e3ef 5303bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5304{
5305 if (index == -1)
5306 {
5307 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5308 if (line)
5309 *height = line->GetSize().y;
5310 else
5311 *height = dc.GetCharHeight();
5312
5313 // -1 means 'the start of the buffer'.
5314 pt = GetPosition();
5315 if (line)
5316 pt = pt + line->GetPosition();
5317
5d7836c4
JS
5318 return true;
5319 }
5320
5321 // The final position in a paragraph is taken to mean the position
5322 // at the start of the next paragraph.
5323 if (index == GetRange().GetEnd())
5324 {
5325 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5326 wxASSERT( parent != NULL );
5327
5328 // Find the height at the next paragraph, if any
5329 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5330 if (line)
5331 {
5332 *height = line->GetSize().y;
5333 pt = line->GetAbsolutePosition();
5334 }
5335 else
5336 {
5337 *height = dc.GetCharHeight();
5338 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5339 pt = wxPoint(indent, GetCachedSize().y);
5340 }
5341
5342 return true;
5343 }
5344
5345 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5346 return false;
5347
5348 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5349 while (node)
5350 {
5351 wxRichTextLine* line = node->GetData();
1e967276
JS
5352 wxRichTextRange lineRange = line->GetAbsoluteRange();
5353 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5354 {
5355 // If this is the last point in the line, and we're forcing the
5356 // returned value to be the start of the next line, do the required
5357 // thing.
1e967276 5358 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5359 {
5360 if (node->GetNext())
5361 {
5362 wxRichTextLine* nextLine = node->GetNext()->GetData();
5363 *height = nextLine->GetSize().y;
5364 pt = nextLine->GetAbsolutePosition();
5365 return true;
5366 }
5367 }
5368
5369 pt.y = line->GetPosition().y + GetPosition().y;
5370
1e967276 5371 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5372 wxSize rangeSize;
5373 int descent = 0;
5374
5375 // We find the size of the line up to this point,
5376 // then we can add this size to the line start position and
5377 // paragraph start position to find the actual position.
5378
8db2e3ef 5379 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5380 {
5381 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5382 *height = line->GetSize().y;
5383
5384 return true;
5385 }
5386
5387 }
5388
5389 node = node->GetNext();
5390 }
5391
5392 return false;
5393}
5394
5395/// Hit-testing: returns a flag indicating hit test details, plus
5396/// information about position
8db2e3ef 5397int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5398{
603f702b
JS
5399 if (!IsShown())
5400 return wxRICHTEXT_HITTEST_NONE;
5401
5402 // If we're in the top-level container, then we can return
5403 // a suitable hit test code even if the point is outside the container area,
5404 // so that we can position the caret sensibly even if we don't
5405 // click on valid content. If we're not at the top-level, and the point
5406 // is not within this paragraph object, then we don't want to stop more
5407 // precise hit-testing from working prematurely, so return immediately.
5408 // NEW STRATEGY: use the parent boundary to test whether we're in the
5409 // right region, not the paragraph, since the paragraph may be positioned
5410 // some way in from where the user clicks.
5411 {
5412 long tmpPos;
5413 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5414 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5415 return wxRICHTEXT_HITTEST_NONE;
5416 }
5417
5418 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5419 while (objNode)
5420 {
5421 wxRichTextObject* child = objNode->GetData();
5422 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0))
5423 {
5424 {
8db2e3ef 5425 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5426 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5427 return hitTest;
5428 }
5429 }
5430
5431 objNode = objNode->GetNext();
5432 }
5433
5d7836c4
JS
5434 wxPoint paraPos = GetPosition();
5435
5436 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5437 while (node)
5438 {
5439 wxRichTextLine* line = node->GetData();
5440 wxPoint linePos = paraPos + line->GetPosition();
5441 wxSize lineSize = line->GetSize();
1e967276 5442 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5443
62381daa 5444 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5445 {
5446 if (pt.x < linePos.x)
5447 {
1e967276 5448 textPosition = lineRange.GetStart();
603f702b
JS
5449 *obj = FindObjectAtPosition(textPosition);
5450 *contextObj = GetContainer();
f262b25c 5451 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5452 }
5453 else if (pt.x >= (linePos.x + lineSize.x))
5454 {
1e967276 5455 textPosition = lineRange.GetEnd();
603f702b
JS
5456 *obj = FindObjectAtPosition(textPosition);
5457 *contextObj = GetContainer();
f262b25c 5458 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5459 }
5460 else
5461 {
2f45f554
JS
5462#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5463 wxArrayInt partialExtents;
5464
5465 wxSize paraSize;
5466 int paraDescent;
5467
5468 // This calculates the partial text extents
8db2e3ef 5469 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
2f45f554
JS
5470
5471 int lastX = linePos.x;
5472 size_t i;
5473 for (i = 0; i < partialExtents.GetCount(); i++)
5474 {
5475 int nextX = partialExtents[i] + linePos.x;
5476
5477 if (pt.x >= lastX && pt.x <= nextX)
5478 {
5479 textPosition = i + lineRange.GetStart(); // minus 1?
5480
603f702b
JS
5481 *obj = FindObjectAtPosition(textPosition);
5482 *contextObj = GetContainer();
5483
2f45f554
JS
5484 // So now we know it's between i-1 and i.
5485 // Let's see if we can be more precise about
5486 // which side of the position it's on.
5487
cdaed652 5488 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5489 if (pt.x >= midPoint)
5490 return wxRICHTEXT_HITTEST_AFTER;
5491 else
5492 return wxRICHTEXT_HITTEST_BEFORE;
5493 }
5494
5495 lastX = nextX;
5496 }
5497#else
5d7836c4
JS
5498 long i;
5499 int lastX = linePos.x;
1e967276 5500 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5501 {
5502 wxSize childSize;
5503 int descent = 0;
7fe8059f 5504
1e967276 5505 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5506
8db2e3ef 5507 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5508
5509 int nextX = childSize.x + linePos.x;
5510
5511 if (pt.x >= lastX && pt.x <= nextX)
5512 {
5513 textPosition = i;
5514
603f702b
JS
5515 *obj = FindObjectAtPosition(textPosition);
5516 *contextObj = GetContainer();
5517
5d7836c4
JS
5518 // So now we know it's between i-1 and i.
5519 // Let's see if we can be more precise about
5520 // which side of the position it's on.
5521
cdaed652 5522 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5523 if (pt.x >= midPoint)
5524 return wxRICHTEXT_HITTEST_AFTER;
5525 else
5526 return wxRICHTEXT_HITTEST_BEFORE;
5527 }
5528 else
5529 {
5530 lastX = nextX;
5531 }
5532 }
2f45f554 5533#endif
5d7836c4
JS
5534 }
5535 }
7fe8059f 5536
5d7836c4
JS
5537 node = node->GetNext();
5538 }
5539
5540 return wxRICHTEXT_HITTEST_NONE;
5541}
5542
5543/// Split an object at this position if necessary, and return
5544/// the previous object, or NULL if inserting at beginning.
5545wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5546{
5547 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5548 while (node)
5549 {
5550 wxRichTextObject* child = node->GetData();
5551
5552 if (pos == child->GetRange().GetStart())
5553 {
5554 if (previousObject)
4d551ad5
JS
5555 {
5556 if (node->GetPrevious())
5557 *previousObject = node->GetPrevious()->GetData();
5558 else
5559 *previousObject = NULL;
5560 }
5d7836c4
JS
5561
5562 return child;
5563 }
5564
5565 if (child->GetRange().Contains(pos))
5566 {
5567 // This should create a new object, transferring part of
5568 // the content to the old object and the rest to the new object.
5569 wxRichTextObject* newObject = child->DoSplit(pos);
5570
5571 // If we couldn't split this object, just insert in front of it.
5572 if (!newObject)
5573 {
5574 // Maybe this is an empty string, try the next one
5575 // return child;
5576 }
5577 else
5578 {
5579 // Insert the new object after 'child'
5580 if (node->GetNext())
5581 m_children.Insert(node->GetNext(), newObject);
5582 else
5583 m_children.Append(newObject);
5584 newObject->SetParent(this);
5585
5586 if (previousObject)
5587 *previousObject = child;
5588
5589 return newObject;
5590 }
5591 }
5592
5593 node = node->GetNext();
5594 }
5595 if (previousObject)
5596 *previousObject = NULL;
5597 return NULL;
5598}
5599
5600/// Move content to a list from obj on
5601void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5602{
5603 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5604 while (node)
5605 {
5606 wxRichTextObject* child = node->GetData();
5607 list.Append(child);
5608
5609 wxRichTextObjectList::compatibility_iterator oldNode = node;
5610
5611 node = node->GetNext();
5612
5613 m_children.DeleteNode(oldNode);
5614 }
5615}
5616
5617/// Add content back from list
5618void wxRichTextParagraph::MoveFromList(wxList& list)
5619{
09f14108 5620 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5621 {
5622 AppendChild((wxRichTextObject*) node->GetData());
5623 }
5624}
5625
5626/// Calculate range
5627void wxRichTextParagraph::CalculateRange(long start, long& end)
5628{
5629 wxRichTextCompositeObject::CalculateRange(start, end);
5630
5631 // Add one for end of paragraph
5632 end ++;
5633
5634 m_range.SetRange(start, end);
5635}
5636
5637/// Find the object at the given position
5638wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5639{
5640 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5641 while (node)
5642 {
5643 wxRichTextObject* obj = node->GetData();
603f702b
JS
5644 if (obj->GetRange().Contains(position) ||
5645 obj->GetRange().GetStart() == position ||
5646 obj->GetRange().GetEnd() == position)
5d7836c4 5647 return obj;
7fe8059f 5648
5d7836c4
JS
5649 node = node->GetNext();
5650 }
5651 return NULL;
5652}
5653
5654/// Get the plain text searching from the start or end of the range.
5655/// The resulting string may be shorter than the range given.
5656bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5657{
5658 text = wxEmptyString;
5659
5660 if (fromStart)
5661 {
5662 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5663 while (node)
5664 {
5665 wxRichTextObject* obj = node->GetData();
5666 if (!obj->GetRange().IsOutside(range))
5667 {
5668 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5669 if (textObj)
5670 {
5671 text += textObj->GetTextForRange(range);
5672 }
5673 else
043c0d58
JS
5674 {
5675 text += wxT(" ");
5676 }
5d7836c4
JS
5677 }
5678
5679 node = node->GetNext();
5680 }
5681 }
5682 else
5683 {
5684 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5685 while (node)
5686 {
5687 wxRichTextObject* obj = node->GetData();
5688 if (!obj->GetRange().IsOutside(range))
5689 {
5690 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5691 if (textObj)
5692 {
5693 text = textObj->GetTextForRange(range) + text;
5694 }
5695 else
043c0d58
JS
5696 {
5697 text = wxT(" ") + text;
5698 }
5d7836c4
JS
5699 }
5700
5701 node = node->GetPrevious();
5702 }
5703 }
5704
5705 return true;
5706}
5707
5708/// Find a suitable wrap position.
8db2e3ef 5709bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5710{
72945e24
JS
5711 if (range.GetLength() <= 0)
5712 return false;
5713
5d7836c4
JS
5714 // Find the first position where the line exceeds the available space.
5715 wxSize sz;
5d7836c4 5716 long breakPosition = range.GetEnd();
ecb5fbf1 5717
31778480
JS
5718#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5719 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 5720 {
31778480 5721 int widthBefore;
5d7836c4 5722
31778480
JS
5723 if (range.GetStart() > GetRange().GetStart())
5724 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5725 else
5726 widthBefore = 0;
5727
5728 size_t i;
43a0d1e1 5729 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 5730 {
31778480 5731 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 5732
72945e24 5733 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 5734 {
31778480
JS
5735 breakPosition = i-1;
5736 break;
ecb5fbf1 5737 }
5d7836c4 5738 }
31778480
JS
5739 }
5740 else
5741#endif
5742 {
5743 // Binary chop for speed
5744 long minPos = range.GetStart();
5745 long maxPos = range.GetEnd();
5746 while (true)
ecb5fbf1 5747 {
31778480
JS
5748 if (minPos == maxPos)
5749 {
5750 int descent = 0;
8db2e3ef 5751 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 5752
31778480
JS
5753 if (sz.x > availableSpace)
5754 breakPosition = minPos - 1;
5755 break;
5756 }
5757 else if ((maxPos - minPos) == 1)
ecb5fbf1 5758 {
31778480 5759 int descent = 0;
8db2e3ef 5760 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5761
5762 if (sz.x > availableSpace)
5763 breakPosition = minPos - 1;
5764 else
5765 {
8db2e3ef 5766 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5767 if (sz.x > availableSpace)
5768 breakPosition = maxPos-1;
5769 }
5770 break;
ecb5fbf1
JS
5771 }
5772 else
5773 {
31778480
JS
5774 long nextPos = minPos + ((maxPos - minPos) / 2);
5775
5776 int descent = 0;
8db2e3ef 5777 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5778
5779 if (sz.x > availableSpace)
5780 {
5781 maxPos = nextPos;
5782 }
5783 else
5784 {
5785 minPos = nextPos;
5786 }
ecb5fbf1
JS
5787 }
5788 }
5d7836c4
JS
5789 }
5790
5791 // Now we know the last position on the line.
5792 // Let's try to find a word break.
5793
5794 wxString plainText;
5795 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
5796 {
ff76711f
JS
5797 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
5798 if (newLinePos != wxNOT_FOUND)
5d7836c4 5799 {
ff76711f
JS
5800 breakPosition = wxMax(0, range.GetStart() + newLinePos);
5801 }
5802 else
5803 {
5804 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
5805 int tabPos = plainText.Find(wxT('\t'), true);
5806 int pos = wxMax(spacePos, tabPos);
5807 if (pos != wxNOT_FOUND)
ff76711f 5808 {
31002e44 5809 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
5810 breakPosition = breakPosition - positionsFromEndOfString;
5811 }
5d7836c4
JS
5812 }
5813 }
5814
5815 wrapPosition = breakPosition;
5816
5817 return true;
5818}
5819
5820/// Get the bullet text for this paragraph.
5821wxString wxRichTextParagraph::GetBulletText()
5822{
5823 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
5824 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
5825 return wxEmptyString;
5826
5827 int number = GetAttributes().GetBulletNumber();
5828
5829 wxString text;
d2d0adc7 5830 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
5831 {
5832 text.Printf(wxT("%d"), number);
5833 }
5834 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
5835 {
5836 // TODO: Unicode, and also check if number > 26
5837 text.Printf(wxT("%c"), (wxChar) (number+64));
5838 }
5839 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
5840 {
5841 // TODO: Unicode, and also check if number > 26
5842 text.Printf(wxT("%c"), (wxChar) (number+96));
5843 }
5844 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
5845 {
59509217 5846 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
5847 }
5848 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
5849 {
59509217
JS
5850 text = wxRichTextDecimalToRoman(number);
5851 text.MakeLower();
5d7836c4
JS
5852 }
5853 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
5854 {
d2d0adc7
JS
5855 text = GetAttributes().GetBulletText();
5856 }
3e541562 5857
d2d0adc7
JS
5858 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
5859 {
5860 // The outline style relies on the text being computed statically,
5861 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5862 // should be stored in the attributes; if not, just use the number for this
5863 // level, as previously computed.
5864 if (!GetAttributes().GetBulletText().IsEmpty())
5865 text = GetAttributes().GetBulletText();
5d7836c4
JS
5866 }
5867
5868 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
5869 {
5870 text = wxT("(") + text + wxT(")");
5871 }
d2d0adc7
JS
5872 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
5873 {
5874 text = text + wxT(")");
5875 }
5876
5d7836c4
JS
5877 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
5878 {
5879 text += wxT(".");
5880 }
5881
5882 return text;
5883}
5884
1e967276
JS
5885/// Allocate or reuse a line object
5886wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
5887{
5888 if (pos < (int) m_cachedLines.GetCount())
5889 {
5890 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
5891 line->Init(this);
5892 return line;
5893 }
5894 else
5895 {
5896 wxRichTextLine* line = new wxRichTextLine(this);
5897 m_cachedLines.Append(line);
5898 return line;
5899 }
5900}
5901
5902/// Clear remaining unused line objects, if any
5903bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
5904{
5905 int cachedLineCount = m_cachedLines.GetCount();
5906 if ((int) cachedLineCount > lineCount)
5907 {
5908 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
5909 {
5910 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
5911 wxRichTextLine* line = node->GetData();
5912 m_cachedLines.Erase(node);
5913 delete line;
5914 }
5915 }
5916 return true;
5917}
5918
fe5aa22c
JS
5919/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5920/// retrieve the actual style.
603f702b 5921wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 5922{
24777478 5923 wxRichTextAttr attr;
603f702b 5924 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
5925 if (buf)
5926 {
5927 attr = buf->GetBasicStyle();
603f702b
JS
5928 if (!includingBoxAttr)
5929 {
5930 attr.GetTextBoxAttr().Reset();
5931 // The background colour will be painted by the container, and we don't
5932 // want to unnecessarily overwrite the background when we're drawing text
5933 // because this may erase the guideline (which appears just under the text
5934 // if there's no padding).
5935 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
5936 }
fe5aa22c
JS
5937 wxRichTextApplyStyle(attr, GetAttributes());
5938 }
5939 else
5940 attr = GetAttributes();
5941
5942 wxRichTextApplyStyle(attr, contentStyle);
5943 return attr;
5944}
5945
5946/// Get combined attributes of the base style and paragraph style.
603f702b 5947wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 5948{
24777478 5949 wxRichTextAttr attr;
603f702b 5950 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
5951 if (buf)
5952 {
5953 attr = buf->GetBasicStyle();
603f702b
JS
5954 if (!includingBoxAttr)
5955 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
5956 wxRichTextApplyStyle(attr, GetAttributes());
5957 }
5958 else
5959 attr = GetAttributes();
5960
5961 return attr;
5962}
5d7836c4 5963
603f702b 5964// Create default tabstop array
cfa3b256
JS
5965void wxRichTextParagraph::InitDefaultTabs()
5966{
5967 // create a default tab list at 10 mm each.
5968 for (int i = 0; i < 20; ++i)
5969 {
5970 sm_defaultTabs.Add(i*100);
5971 }
5972}
5973
603f702b 5974// Clear default tabstop array
cfa3b256
JS
5975void wxRichTextParagraph::ClearDefaultTabs()
5976{
5977 sm_defaultTabs.Clear();
5978}
5979
8db2e3ef 5980void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
5981{
5982 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
5983 while (node)
5984 {
bec80f4f 5985 wxRichTextObject* anchored = node->GetData();
07d4142f 5986 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652
VZ
5987 {
5988 wxSize size;
5989 int descent, x = 0;
8db2e3ef 5990 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
bec80f4f 5991
24777478 5992 int offsetY = 0;
603f702b 5993 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
5994 {
5995 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5996 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
5997 {
5998 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
5999 }
6000 }
bec80f4f 6001
24777478 6002 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6003
cdaed652 6004 /* Update the offset */
24777478
JS
6005 int newOffsetY = pos - rect.y;
6006 if (newOffsetY != offsetY)
6007 {
6008 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6009 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6010 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6011 }
cdaed652 6012
24777478 6013 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6014 x = rect.x;
24777478 6015 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6016 x = rect.x + rect.width - size.x;
24777478 6017
cdaed652
VZ
6018 anchored->SetPosition(wxPoint(x, pos));
6019 anchored->SetCachedSize(size);
6020 floatCollector->CollectFloat(this, anchored);
6021 }
6022
6023 node = node->GetNext();
6024 }
6025}
6026
603f702b 6027// Get the first position from pos that has a line break character.
ff76711f
JS
6028long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6029{
6030 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6031 while (node)
6032 {
6033 wxRichTextObject* obj = node->GetData();
6034 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6035 {
6036 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6037 if (textObj)
6038 {
6039 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6040 if (breakPos > -1)
6041 return breakPos;
6042 }
6043 }
6044 node = node->GetNext();
6045 }
6046 return -1;
6047}
cfa3b256 6048
5d7836c4
JS
6049/*!
6050 * wxRichTextLine
6051 * This object represents a line in a paragraph, and stores
6052 * offsets from the start of the paragraph representing the
6053 * start and end positions of the line.
6054 */
6055
6056wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6057{
1e967276 6058 Init(parent);
5d7836c4
JS
6059}
6060
6061/// Initialisation
1e967276 6062void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6063{
1e967276
JS
6064 m_parent = parent;
6065 m_range.SetRange(-1, -1);
6066 m_pos = wxPoint(0, 0);
6067 m_size = wxSize(0, 0);
5d7836c4 6068 m_descent = 0;
2f45f554
JS
6069#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6070 m_objectSizes.Clear();
6071#endif
5d7836c4
JS
6072}
6073
6074/// Copy
6075void wxRichTextLine::Copy(const wxRichTextLine& obj)
6076{
6077 m_range = obj.m_range;
2f45f554
JS
6078#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6079 m_objectSizes = obj.m_objectSizes;
6080#endif
5d7836c4
JS
6081}
6082
6083/// Get the absolute object position
6084wxPoint wxRichTextLine::GetAbsolutePosition() const
6085{
6086 return m_parent->GetPosition() + m_pos;
6087}
6088
1e967276
JS
6089/// Get the absolute range
6090wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6091{
6092 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6093 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6094 return range;
6095}
6096
5d7836c4
JS
6097/*!
6098 * wxRichTextPlainText
6099 * This object represents a single piece of text.
6100 */
6101
6102IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6103
24777478 6104wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6105 wxRichTextObject(parent)
6106{
5d7836c4
JS
6107 if (style)
6108 SetAttributes(*style);
6109
6110 m_text = text;
6111}
6112
cfa3b256
JS
6113#define USE_KERNING_FIX 1
6114
4794d69c
JS
6115// If insufficient tabs are defined, this is the tab width used
6116#define WIDTH_FOR_DEFAULT_TABS 50
6117
5d7836c4 6118/// Draw the item
8db2e3ef 6119bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6120{
fe5aa22c
JS
6121 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6122 wxASSERT (para != NULL);
6123
603f702b 6124 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6125 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6126
6127 // Let's make the assumption for now that for content in a paragraph, including
6128 // text, we never have a discontinuous selection. So we only deal with a
6129 // single range.
6130 wxRichTextRange selectionRange;
6131 if (selection.IsValid())
6132 {
6133 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6134 if (selectionRanges.GetCount() > 0)
6135 selectionRange = selectionRanges[0];
6136 else
6137 selectionRange = wxRICHTEXT_NO_SELECTION;
6138 }
6139 else
6140 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6141
5d7836c4
JS
6142 int offset = GetRange().GetStart();
6143
ff76711f
JS
6144 // Replace line break characters with spaces
6145 wxString str = m_text;
6146 wxString toRemove = wxRichTextLineBreakChar;
6147 str.Replace(toRemove, wxT(" "));
c025e094
JS
6148 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6149 str.MakeUpper();
3e541562 6150
5d7836c4 6151 long len = range.GetLength();
ff76711f 6152 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6153
5d7836c4
JS
6154 // Test for the optimized situations where all is selected, or none
6155 // is selected.
6156
30bf7630
JS
6157 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6158 wxCheckSetFont(dc, textFont);
6159 int charHeight = dc.GetCharHeight();
6160
6161 int x, y;
a1b806b9 6162 if ( textFont.IsOk() )
30bf7630
JS
6163 {
6164 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6165 {
6166 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6167 textFont.SetPointSize( static_cast<int>(size) );
6168 x = rect.x;
6169 y = rect.y;
6170 wxCheckSetFont(dc, textFont);
6171 }
6172 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6173 {
6174 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6175 textFont.SetPointSize( static_cast<int>(size) );
6176 x = rect.x;
6177 int sub_height = static_cast<int>( static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6178 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6179 wxCheckSetFont(dc, textFont);
6180 }
6181 else
6182 {
6183 x = rect.x;
6184 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6185 }
6186 }
6187 else
6188 {
6189 x = rect.x;
6190 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6191 }
5d7836c4 6192
603f702b
JS
6193 // TODO: new selection code
6194
5d7836c4
JS
6195 // (a) All selected.
6196 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6197 {
fe5aa22c 6198 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6199 }
6200 // (b) None selected.
6201 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6202 {
6203 // Draw all unselected
fe5aa22c 6204 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6205 }
6206 else
6207 {
6208 // (c) Part selected, part not
6209 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6210
04ee05f9 6211 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6212
5d7836c4
JS
6213 // 1. Initial unselected chunk, if any, up until start of selection.
6214 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6215 {
6216 int r1 = range.GetStart();
6217 int s1 = selectionRange.GetStart()-1;
6218 int fragmentLen = s1 - r1 + 1;
6219 if (fragmentLen < 0)
af588446 6220 {
5d7836c4 6221 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6222 }
ff76711f 6223 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6224
fe5aa22c 6225 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6226
6227#if USE_KERNING_FIX
6228 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6229 {
6230 // Compensate for kerning difference
ff76711f
JS
6231 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6232 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6233
cfa3b256
JS
6234 wxCoord w1, h1, w2, h2, w3, h3;
6235 dc.GetTextExtent(stringFragment, & w1, & h1);
6236 dc.GetTextExtent(stringFragment2, & w2, & h2);
6237 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6238
cfa3b256
JS
6239 int kerningDiff = (w1 + w3) - w2;
6240 x = x - kerningDiff;
6241 }
6242#endif
5d7836c4
JS
6243 }
6244
6245 // 2. Selected chunk, if any.
6246 if (selectionRange.GetEnd() >= range.GetStart())
6247 {
6248 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6249 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6250
6251 int fragmentLen = s2 - s1 + 1;
6252 if (fragmentLen < 0)
af588446 6253 {
5d7836c4 6254 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6255 }
ff76711f 6256 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6257
fe5aa22c 6258 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6259
6260#if USE_KERNING_FIX
6261 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6262 {
6263 // Compensate for kerning difference
ff76711f
JS
6264 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6265 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6266
cfa3b256
JS
6267 wxCoord w1, h1, w2, h2, w3, h3;
6268 dc.GetTextExtent(stringFragment, & w1, & h1);
6269 dc.GetTextExtent(stringFragment2, & w2, & h2);
6270 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6271
cfa3b256
JS
6272 int kerningDiff = (w1 + w3) - w2;
6273 x = x - kerningDiff;
6274 }
6275#endif
5d7836c4
JS
6276 }
6277
6278 // 3. Remaining unselected chunk, if any
6279 if (selectionRange.GetEnd() < range.GetEnd())
6280 {
6281 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6282 int r2 = range.GetEnd();
6283
6284 int fragmentLen = r2 - s2 + 1;
6285 if (fragmentLen < 0)
af588446 6286 {
5d7836c4 6287 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6288 }
ff76711f 6289 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6290
fe5aa22c 6291 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6292 }
5d7836c4
JS
6293 }
6294
6295 return true;
6296}
61399247 6297
24777478 6298bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6299{
cfa3b256
JS
6300 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6301
6302 wxArrayInt tabArray;
6303 int tabCount;
6304 if (hasTabs)
ab14c7aa 6305 {
cfa3b256
JS
6306 if (attr.GetTabs().IsEmpty())
6307 tabArray = wxRichTextParagraph::GetDefaultTabs();
6308 else
6309 tabArray = attr.GetTabs();
6310 tabCount = tabArray.GetCount();
6311
6312 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6313 {
cfa3b256
JS
6314 int pos = tabArray[i];
6315 pos = ConvertTenthsMMToPixels(dc, pos);
6316 tabArray[i] = pos;
7f0d9d71
JS
6317 }
6318 }
cfa3b256
JS
6319 else
6320 tabCount = 0;
ab14c7aa 6321
cfa3b256
JS
6322 int nextTabPos = -1;
6323 int tabPos = -1;
7f0d9d71 6324 wxCoord w, h;
ab14c7aa 6325
cfa3b256 6326 if (selected)
ab14c7aa 6327 {
0ec6da02
JS
6328 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6329 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6330
ecb5fbf1
JS
6331 wxCheckSetBrush(dc, wxBrush(highlightColour));
6332 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6333 dc.SetTextForeground(highlightTextColour);
04ee05f9 6334 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6335 }
ab14c7aa
JS
6336 else
6337 {
fe5aa22c 6338 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6339
f0e9eda2
JS
6340 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6341 {
04ee05f9 6342 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6343 dc.SetTextBackground(attr.GetBackgroundColour());
6344 }
6345 else
04ee05f9 6346 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6347 }
3e541562 6348
925a662a 6349 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6350 while (hasTabs)
ab14c7aa
JS
6351 {
6352 // the string has a tab
7f0d9d71
JS
6353 // break up the string at the Tab
6354 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6355 str = str.AfterFirst(wxT('\t'));
6356 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6357 tabPos = x + w;
7f0d9d71 6358 bool not_found = true;
cfa3b256 6359 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6360 {
015d0446 6361 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6362
6363 // Find the next tab position.
6364 // Even if we're at the end of the tab array, we must still draw the chunk.
6365
6366 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6367 {
4794d69c
JS
6368 if (nextTabPos <= tabPos)
6369 {
6370 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6371 nextTabPos = tabPos + defaultTabWidth;
6372 }
6373
7f0d9d71 6374 not_found = false;
ab14c7aa
JS
6375 if (selected)
6376 {
cfa3b256 6377 w = nextTabPos - x;
7f0d9d71 6378 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6379 dc.DrawRectangle(selRect);
7f0d9d71
JS
6380 }
6381 dc.DrawText(stringChunk, x, y);
42688aea
JS
6382
6383 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6384 {
6385 wxPen oldPen = dc.GetPen();
ecb5fbf1 6386 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6387 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6388 wxCheckSetPen(dc, oldPen);
42688aea
JS
6389 }
6390
cfa3b256 6391 x = nextTabPos;
7f0d9d71
JS
6392 }
6393 }
cfa3b256 6394 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6395 }
61399247 6396
cfa3b256 6397 if (!str.IsEmpty())
ab14c7aa 6398 {
cfa3b256
JS
6399 dc.GetTextExtent(str, & w, & h);
6400 if (selected)
6401 {
6402 wxRect selRect(x, rect.y, w, rect.GetHeight());
6403 dc.DrawRectangle(selRect);
6404 }
6405 dc.DrawText(str, x, y);
42688aea
JS
6406
6407 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6408 {
6409 wxPen oldPen = dc.GetPen();
ecb5fbf1 6410 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6411 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6412 wxCheckSetPen(dc, oldPen);
42688aea
JS
6413 }
6414
cfa3b256 6415 x += w;
7f0d9d71 6416 }
7f0d9d71 6417 return true;
5d7836c4 6418
7f0d9d71 6419}
fe5aa22c 6420
5d7836c4 6421/// Lay the item out
8db2e3ef 6422bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6423{
ecb5fbf1
JS
6424 // Only lay out if we haven't already cached the size
6425 if (m_size.x == -1)
8db2e3ef 6426 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6427 m_maxSize = m_size;
6428 // Eventually we want to have a reasonable estimate of minimum size.
6429 m_minSize = wxSize(0, 0);
5d7836c4
JS
6430 return true;
6431}
6432
6433/// Copy
6434void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6435{
6436 wxRichTextObject::Copy(obj);
6437
6438 m_text = obj.m_text;
6439}
6440
6441/// Get/set the object size for the given range. Returns false if the range
6442/// is invalid for this object.
8db2e3ef 6443bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
6444{
6445 if (!range.IsWithin(GetRange()))
6446 return false;
6447
fe5aa22c
JS
6448 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6449 wxASSERT (para != NULL);
603f702b 6450
925a662a 6451 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6452
24777478 6453 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6454 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6455
5d7836c4
JS
6456 // Always assume unformatted text, since at this level we have no knowledge
6457 // of line breaks - and we don't need it, since we'll calculate size within
6458 // formatted text by doing it in chunks according to the line ranges
6459
30bf7630 6460 bool bScript(false);
44cc96a8 6461 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6462 if (font.IsOk())
30bf7630
JS
6463 {
6464 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6465 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6466 {
6467 wxFont textFont = font;
6468 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6469 textFont.SetPointSize( static_cast<int>(size) );
6470 wxCheckSetFont(dc, textFont);
6471 bScript = true;
6472 }
6473 else
6474 {
6475 wxCheckSetFont(dc, font);
6476 }
6477 }
5d7836c4 6478
109bfc88 6479 bool haveDescent = false;
5d7836c4
JS
6480 int startPos = range.GetStart() - GetRange().GetStart();
6481 long len = range.GetLength();
3e541562 6482
ff76711f
JS
6483 wxString str(m_text);
6484 wxString toReplace = wxRichTextLineBreakChar;
6485 str.Replace(toReplace, wxT(" "));
6486
6487 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea
JS
6488
6489 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6490 stringChunk.MakeUpper();
6491
5d7836c4 6492 wxCoord w, h;
7f0d9d71 6493 int width = 0;
cfa3b256 6494 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6495 {
6496 // the string has a tab
cfa3b256
JS
6497 wxArrayInt tabArray;
6498 if (textAttr.GetTabs().IsEmpty())
6499 tabArray = wxRichTextParagraph::GetDefaultTabs();
6500 else
6501 tabArray = textAttr.GetTabs();
ab14c7aa 6502
cfa3b256 6503 int tabCount = tabArray.GetCount();
41a85215 6504
cfa3b256 6505 for (int i = 0; i < tabCount; ++i)
61399247 6506 {
cfa3b256
JS
6507 int pos = tabArray[i];
6508 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6509 tabArray[i] = pos;
7f0d9d71 6510 }
41a85215 6511
cfa3b256 6512 int nextTabPos = -1;
61399247 6513
ab14c7aa
JS
6514 while (stringChunk.Find(wxT('\t')) >= 0)
6515 {
109bfc88
JS
6516 int absoluteWidth = 0;
6517
ab14c7aa 6518 // the string has a tab
7f0d9d71
JS
6519 // break up the string at the Tab
6520 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6521 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6522
31778480
JS
6523 if (partialExtents)
6524 {
109bfc88
JS
6525 int oldWidth;
6526 if (partialExtents->GetCount() > 0)
6527 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6528 else
6529 oldWidth = 0;
6530
31778480
JS
6531 // Add these partial extents
6532 wxArrayInt p;
6533 dc.GetPartialTextExtents(stringFragment, p);
6534 size_t j;
6535 for (j = 0; j < p.GetCount(); j++)
6536 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6537
6538 if (partialExtents->GetCount() > 0)
925a662a 6539 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6540 else
925a662a 6541 absoluteWidth = relativeX;
109bfc88
JS
6542 }
6543 else
6544 {
6545 dc.GetTextExtent(stringFragment, & w, & h);
6546 width += w;
603f702b 6547 absoluteWidth = width + relativeX;
109bfc88 6548 haveDescent = true;
31778480
JS
6549 }
6550
cfa3b256
JS
6551 bool notFound = true;
6552 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6553 {
cfa3b256 6554 nextTabPos = tabArray.Item(i);
4794d69c
JS
6555
6556 // Find the next tab position.
6557 // Even if we're at the end of the tab array, we must still process the chunk.
6558
6559 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6560 {
4794d69c
JS
6561 if (nextTabPos <= absoluteWidth)
6562 {
6563 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6564 nextTabPos = absoluteWidth + defaultTabWidth;
6565 }
6566
cfa3b256 6567 notFound = false;
925a662a 6568 width = nextTabPos - relativeX;
31778480
JS
6569
6570 if (partialExtents)
6571 partialExtents->Add(width);
7f0d9d71
JS
6572 }
6573 }
6574 }
6575 }
30bf7630 6576
31778480
JS
6577 if (!stringChunk.IsEmpty())
6578 {
31778480
JS
6579 if (partialExtents)
6580 {
109bfc88
JS
6581 int oldWidth;
6582 if (partialExtents->GetCount() > 0)
6583 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6584 else
6585 oldWidth = 0;
6586
31778480
JS
6587 // Add these partial extents
6588 wxArrayInt p;
6589 dc.GetPartialTextExtents(stringChunk, p);
6590 size_t j;
6591 for (j = 0; j < p.GetCount(); j++)
6592 partialExtents->Add(oldWidth + p[j]);
6593 }
109bfc88
JS
6594 else
6595 {
6596 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6597 width += w;
6598 haveDescent = true;
6599 }
6600 }
6601
6602 if (partialExtents)
6603 {
6604 int charHeight = dc.GetCharHeight();
6605 if ((*partialExtents).GetCount() > 0)
6606 w = (*partialExtents)[partialExtents->GetCount()-1];
6607 else
6608 w = 0;
6609 size = wxSize(w, charHeight);
6610 }
6611 else
6612 {
6613 size = wxSize(width, dc.GetCharHeight());
31778480 6614 }
30bf7630 6615
109bfc88
JS
6616 if (!haveDescent)
6617 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6618
30bf7630
JS
6619 if ( bScript )
6620 dc.SetFont(font);
6621
5d7836c4
JS
6622 return true;
6623}
6624
6625/// Do a split, returning an object containing the second part, and setting
6626/// the first part in 'this'.
6627wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6628{
ff76711f 6629 long index = pos - GetRange().GetStart();
3e541562 6630
28f92d74 6631 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6632 return NULL;
6633
6634 wxString firstPart = m_text.Mid(0, index);
6635 wxString secondPart = m_text.Mid(index);
6636
6637 m_text = firstPart;
6638
6639 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6640 newObject->SetAttributes(GetAttributes());
8db2e3ef 6641 newObject->SetProperties(GetProperties());
5d7836c4
JS
6642
6643 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6644 GetRange().SetEnd(pos-1);
3e541562 6645
5d7836c4
JS
6646 return newObject;
6647}
6648
6649/// Calculate range
6650void wxRichTextPlainText::CalculateRange(long start, long& end)
6651{
28f92d74 6652 end = start + m_text.length() - 1;
5d7836c4
JS
6653 m_range.SetRange(start, end);
6654}
6655
6656/// Delete range
6657bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6658{
6659 wxRichTextRange r = range;
6660
6661 r.LimitTo(GetRange());
6662
6663 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6664 {
6665 m_text.Empty();
6666 return true;
6667 }
6668
6669 long startIndex = r.GetStart() - GetRange().GetStart();
6670 long len = r.GetLength();
6671
6672 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6673 return true;
6674}
6675
6676/// Get text for the given range.
6677wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6678{
6679 wxRichTextRange r = range;
6680
6681 r.LimitTo(GetRange());
6682
6683 long startIndex = r.GetStart() - GetRange().GetStart();
6684 long len = r.GetLength();
6685
6686 return m_text.Mid(startIndex, len);
6687}
6688
6689/// Returns true if this object can merge itself with the given one.
6690bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
6691{
6692 return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) &&
8db2e3ef 6693 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
5d7836c4
JS
6694}
6695
6696/// Returns true if this object merged itself with the given one.
6697/// The calling code will then delete the given object.
6698bool wxRichTextPlainText::Merge(wxRichTextObject* object)
6699{
6700 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
6701 wxASSERT( textObject != NULL );
6702
6703 if (textObject)
6704 {
6705 m_text += textObject->GetText();
99404ab0 6706 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
6707 return true;
6708 }
6709 else
6710 return false;
6711}
6712
6713/// Dump to output stream for debugging
6714void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
6715{
6716 wxRichTextObject::Dump(stream);
6717 stream << m_text << wxT("\n");
6718}
6719
ff76711f
JS
6720/// Get the first position from pos that has a line break character.
6721long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
6722{
6723 int i;
6724 int len = m_text.length();
6725 int startPos = pos - m_range.GetStart();
6726 for (i = startPos; i < len; i++)
6727 {
6728 wxChar ch = m_text[i];
6729 if (ch == wxRichTextLineBreakChar)
6730 {
6731 return i + m_range.GetStart();
6732 }
6733 }
6734 return -1;
6735}
6736
5d7836c4
JS
6737/*!
6738 * wxRichTextBuffer
6739 * This is a kind of box, used to represent the whole buffer
6740 */
6741
6742IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
6743
d2d0adc7 6744wxList wxRichTextBuffer::sm_handlers;
8db2e3ef 6745wxList wxRichTextBuffer::sm_drawingHandlers;
d2d0adc7
JS
6746wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
6747int wxRichTextBuffer::sm_bulletRightMargin = 20;
6748float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
5d7836c4
JS
6749
6750/// Initialisation
6751void wxRichTextBuffer::Init()
6752{
6753 m_commandProcessor = new wxCommandProcessor;
6754 m_styleSheet = NULL;
6755 m_modified = false;
6756 m_batchedCommandDepth = 0;
6757 m_batchedCommand = NULL;
6758 m_suppressUndo = 0;
d2d0adc7 6759 m_handlerFlags = 0;
44219ff0 6760 m_scale = 1.0;
5d7836c4
JS
6761}
6762
6763/// Initialisation
6764wxRichTextBuffer::~wxRichTextBuffer()
6765{
6766 delete m_commandProcessor;
6767 delete m_batchedCommand;
6768
6769 ClearStyleStack();
d2d0adc7 6770 ClearEventHandlers();
5d7836c4
JS
6771}
6772
85d8909b 6773void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 6774{
85d8909b 6775 Reset();
3e541562 6776
5d7836c4 6777 GetCommandProcessor()->ClearCommands();
5d7836c4 6778
5d7836c4 6779 Modify(false);
1e967276 6780 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
6781}
6782
0ca07313
JS
6783void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
6784{
6785 wxRichTextParagraphLayoutBox::Copy(obj);
6786
6787 m_styleSheet = obj.m_styleSheet;
6788 m_modified = obj.m_modified;
bec80f4f
JS
6789 m_batchedCommandDepth = 0;
6790 if (m_batchedCommand)
6791 delete m_batchedCommand;
6792 m_batchedCommand = NULL;
0ca07313 6793 m_suppressUndo = obj.m_suppressUndo;
603f702b 6794 m_invalidRange = obj.m_invalidRange;
0ca07313
JS
6795}
6796
38f833b1
JS
6797/// Push style sheet to top of stack
6798bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
6799{
6800 if (m_styleSheet)
6801 styleSheet->InsertSheet(m_styleSheet);
6802
6803 SetStyleSheet(styleSheet);
41a85215 6804
38f833b1
JS
6805 return true;
6806}
6807
6808/// Pop style sheet from top of stack
6809wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
6810{
6811 if (m_styleSheet)
6812 {
6813 wxRichTextStyleSheet* oldSheet = m_styleSheet;
6814 m_styleSheet = oldSheet->GetNextSheet();
6815 oldSheet->Unlink();
41a85215 6816
38f833b1
JS
6817 return oldSheet;
6818 }
6819 else
6820 return NULL;
6821}
6822
0ca07313
JS
6823/// Submit command to insert paragraphs
6824bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
6825{
4e63bfb9 6826 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
6827}
6828
6829/// Submit command to insert paragraphs
4e63bfb9 6830bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
6831{
6832 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 6833
0ca07313 6834 action->GetNewParagraphs() = paragraphs;
59509217 6835
0ca07313
JS
6836 action->SetPosition(pos);
6837
603f702b 6838 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
6839 if (!paragraphs.GetPartialParagraph())
6840 range.SetEnd(range.GetEnd()+1);
6841
0ca07313 6842 // Set the range we'll need to delete in Undo
99404ab0 6843 action->SetRange(range);
0ca07313 6844
603f702b 6845 buffer->SubmitAction(action);
0ca07313
JS
6846
6847 return true;
6848}
6849
5d7836c4 6850/// Submit command to insert the given text
fe5aa22c 6851bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 6852{
4e63bfb9 6853 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
6854}
6855
6856/// Submit command to insert the given text
4e63bfb9 6857bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
6858{
6859 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 6860
24777478
JS
6861 wxRichTextAttr* p = NULL;
6862 wxRichTextAttr paraAttr;
fe5aa22c
JS
6863 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6864 {
7c081bd2 6865 // Get appropriate paragraph style
603f702b 6866 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
6867 if (!paraAttr.IsDefault())
6868 p = & paraAttr;
6869 }
6870
fe5aa22c 6871 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 6872
603f702b 6873 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 6874
6636ef8d 6875 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
6876 {
6877 // Don't count the newline when undoing
6878 length --;
5d7836c4 6879 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 6880 }
6636ef8d 6881 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 6882 length --;
5d7836c4
JS
6883
6884 action->SetPosition(pos);
6885
6886 // Set the range we'll need to delete in Undo
0ca07313 6887 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 6888
603f702b 6889 buffer->SubmitAction(action);
7fe8059f 6890
5d7836c4
JS
6891 return true;
6892}
6893
6894/// Submit command to insert the given text
fe5aa22c 6895bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 6896{
4e63bfb9 6897 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
6898}
6899
6900/// Submit command to insert the given text
4e63bfb9 6901bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
6902{
6903 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 6904
24777478
JS
6905 wxRichTextAttr* p = NULL;
6906 wxRichTextAttr paraAttr;
fe5aa22c
JS
6907 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6908 {
603f702b 6909 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
6910 if (!paraAttr.IsDefault())
6911 p = & paraAttr;
6912 }
6913
603f702b 6914 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f
WS
6915
6916 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
6917 action->GetNewParagraphs().AppendChild(newPara);
6918 action->GetNewParagraphs().UpdateRanges();
6919 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
6920 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
6921 long pos1 = pos;
6922
6c0ea513
JS
6923 if (p)
6924 newPara->SetAttributes(*p);
6925
c025e094
JS
6926 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
6927 {
6928 if (para && para->GetRange().GetEnd() == pos)
6929 pos1 ++;
e2d0875a
JS
6930
6931 // Now see if we need to number the paragraph.
6c0ea513 6932 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
6933 {
6934 wxRichTextAttr numberingAttr;
6935 if (FindNextParagraphNumber(para, numberingAttr))
6936 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
6937 }
c025e094
JS
6938 }
6939
5d7836c4
JS
6940 action->SetPosition(pos);
6941
c025e094 6942 // Use the default character style
99404ab0 6943 // Use the default character style
603f702b 6944 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
6945 {
6946 // Check whether the default style merely reflects the paragraph/basic style,
6947 // in which case don't apply it.
603f702b 6948 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
24777478 6949 wxRichTextAttr toApply;
c025e094
JS
6950 if (para)
6951 {
603f702b 6952 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 6953 wxRichTextAttr newAttr;
c025e094
JS
6954 // This filters out attributes that are accounted for by the current
6955 // paragraph/basic style
6956 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
6957 }
6958 else
6959 toApply = defaultStyle;
6960
6961 if (!toApply.IsDefault())
6962 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
6963 }
99404ab0 6964
5d7836c4 6965 // Set the range we'll need to delete in Undo
c025e094 6966 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 6967
603f702b 6968 buffer->SubmitAction(action);
7fe8059f 6969
5d7836c4
JS
6970 return true;
6971}
6972
6973/// Submit command to insert the given image
24777478
JS
6974bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
6975 const wxRichTextAttr& textAttr)
5d7836c4 6976{
4e63bfb9 6977 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
6978}
6979
6980/// Submit command to insert the given image
4e63bfb9
JS
6981bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
6982 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
6983 const wxRichTextAttr& textAttr)
6984{
6985 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 6986
24777478
JS
6987 wxRichTextAttr* p = NULL;
6988 wxRichTextAttr paraAttr;
fe5aa22c
JS
6989 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6990 {
603f702b 6991 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
6992 if (!paraAttr.IsDefault())
6993 p = & paraAttr;
6994 }
6995
603f702b 6996 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 6997
5d7836c4 6998 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
6999 if (p)
7000 newPara->SetAttributes(*p);
7001
5d7836c4
JS
7002 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7003 newPara->AppendChild(imageObject);
24777478 7004 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7005 action->GetNewParagraphs().AppendChild(newPara);
7006 action->GetNewParagraphs().UpdateRanges();
7007
7008 action->GetNewParagraphs().SetPartialParagraph(true);
7009
7010 action->SetPosition(pos);
7011
7012 // Set the range we'll need to delete in Undo
7013 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7014
603f702b 7015 buffer->SubmitAction(action);
7fe8059f 7016
5d7836c4
JS
7017 return true;
7018}
7019
cdaed652 7020// Insert an object with no change of it
603f702b
JS
7021wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7022{
4e63bfb9 7023 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7024}
7025
7026// Insert an object with no change of it
4e63bfb9 7027wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7028{
603f702b 7029 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7030
24777478
JS
7031 wxRichTextAttr* p = NULL;
7032 wxRichTextAttr paraAttr;
cdaed652
VZ
7033 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7034 {
603f702b 7035 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7036 if (!paraAttr.IsDefault())
7037 p = & paraAttr;
7038 }
7039
603f702b 7040 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652
VZ
7041
7042 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7043 if (p)
7044 newPara->SetAttributes(*p);
7045
7046 newPara->AppendChild(object);
7047 action->GetNewParagraphs().AppendChild(newPara);
7048 action->GetNewParagraphs().UpdateRanges();
7049
7050 action->GetNewParagraphs().SetPartialParagraph(true);
7051
7052 action->SetPosition(pos);
7053
7054 // Set the range we'll need to delete in Undo
7055 action->SetRange(wxRichTextRange(pos, pos));
7056
603f702b 7057 buffer->SubmitAction(action);
cdaed652 7058
603f702b
JS
7059 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7060 return obj;
cdaed652 7061}
603f702b 7062
fe5aa22c
JS
7063/// Get the style that is appropriate for a new paragraph at this position.
7064/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7065/// style.
603f702b 7066wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7067{
7068 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7069 if (para)
7070 {
24777478 7071 wxRichTextAttr attr;
d2d0adc7 7072 bool foundAttributes = false;
3e541562 7073
d2d0adc7 7074 // Look for a matching paragraph style
603f702b 7075 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7076 {
603f702b 7077 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7078 if (paraDef)
fe5aa22c 7079 {
caad0109
JS
7080 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7081 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7082 {
603f702b 7083 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7084 if (nextParaDef)
7085 {
7086 foundAttributes = true;
603f702b 7087 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7088 }
7089 }
3e541562 7090
d2d0adc7
JS
7091 // If we didn't find the 'next style', use this style instead.
7092 if (!foundAttributes)
7093 {
7094 foundAttributes = true;
603f702b 7095 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7096 }
fe5aa22c
JS
7097 }
7098 }
e2d0875a
JS
7099
7100 // Also apply list style if present
603f702b 7101 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7102 {
603f702b 7103 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7104 if (listDef)
7105 {
7106 int thisIndent = para->GetAttributes().GetLeftIndent();
7107 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7108
7109 // Apply the overall list style, and item style for this level
603f702b 7110 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7111 wxRichTextApplyStyle(attr, listStyle);
7112 attr.SetOutlineLevel(thisLevel);
7113 if (para->GetAttributes().HasBulletNumber())
7114 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7115 }
34b4899d 7116 }
e2d0875a 7117
d2d0adc7
JS
7118 if (!foundAttributes)
7119 {
7120 attr = para->GetAttributes();
7121 int flags = attr.GetFlags();
fe5aa22c 7122
d2d0adc7
JS
7123 // Eliminate character styles
7124 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7125 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7126 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7127 attr.SetFlags(flags);
7128 }
3e541562 7129
fe5aa22c
JS
7130 return attr;
7131 }
7132 else
24777478 7133 return wxRichTextAttr();
fe5aa22c
JS
7134}
7135
5d7836c4 7136/// Submit command to delete this range
12cc29c5 7137bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7138{
603f702b
JS
7139 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7140}
7141
7142/// Submit command to delete this range
7143bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7144{
7145 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7146
12cc29c5 7147 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7148
7149 // Set the range to delete
7150 action->SetRange(range);
7fe8059f 7151
5d7836c4
JS
7152 // Copy the fragment that we'll need to restore in Undo
7153 CopyFragment(range, action->GetOldParagraphs());
7154
6c0ea513
JS
7155 // See if we're deleting a paragraph marker, in which case we need to
7156 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7157 if (range.GetStart() == range.GetEnd())
5d7836c4 7158 {
6c0ea513
JS
7159 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7160 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7161 {
6c0ea513
JS
7162 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7163 if (nextPara && nextPara != para)
5d7836c4 7164 {
6c0ea513
JS
7165 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7166 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7167 }
7168 }
7169 }
7170
603f702b 7171 buffer->SubmitAction(action);
7fe8059f 7172
5d7836c4
JS
7173 return true;
7174}
7175
7176/// Collapse undo/redo commands
7177bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7178{
7179 if (m_batchedCommandDepth == 0)
7180 {
7181 wxASSERT(m_batchedCommand == NULL);
7182 if (m_batchedCommand)
7183 {
0745f364 7184 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7185 }
7186 m_batchedCommand = new wxRichTextCommand(cmdName);
7187 }
7188
7fe8059f 7189 m_batchedCommandDepth ++;
5d7836c4
JS
7190
7191 return true;
7192}
7193
7194/// Collapse undo/redo commands
7195bool wxRichTextBuffer::EndBatchUndo()
7196{
7197 m_batchedCommandDepth --;
7198
7199 wxASSERT(m_batchedCommandDepth >= 0);
7200 wxASSERT(m_batchedCommand != NULL);
7201
7202 if (m_batchedCommandDepth == 0)
7203 {
0745f364 7204 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7205 m_batchedCommand = NULL;
7206 }
7207
7208 return true;
7209}
7210
7211/// Submit immediately, or delay according to whether collapsing is on
7212bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7213{
cc2aecde
JS
7214 if (action && !action->GetNewParagraphs().IsEmpty())
7215 PrepareContent(action->GetNewParagraphs());
7216
5d7836c4 7217 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7218 {
7219 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7220 cmd->AddAction(action);
7221 cmd->Do();
7222 cmd->GetActions().Clear();
7223 delete cmd;
7224
5d7836c4 7225 m_batchedCommand->AddAction(action);
0745f364 7226 }
5d7836c4
JS
7227 else
7228 {
7229 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7230 cmd->AddAction(action);
7231
7232 // Only store it if we're not suppressing undo.
7233 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7234 }
7235
7236 return true;
7237}
7238
7239/// Begin suppressing undo/redo commands.
7240bool wxRichTextBuffer::BeginSuppressUndo()
7241{
7fe8059f 7242 m_suppressUndo ++;
5d7836c4
JS
7243
7244 return true;
7245}
7246
7247/// End suppressing undo/redo commands.
7248bool wxRichTextBuffer::EndSuppressUndo()
7249{
7fe8059f 7250 m_suppressUndo --;
5d7836c4
JS
7251
7252 return true;
7253}
7254
7255/// Begin using a style
24777478 7256bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7257{
24777478 7258 wxRichTextAttr newStyle(GetDefaultStyle());
5d7836c4
JS
7259
7260 // Save the old default style
24777478 7261 m_attributeStack.Append((wxObject*) new wxRichTextAttr(GetDefaultStyle()));
5d7836c4
JS
7262
7263 wxRichTextApplyStyle(newStyle, style);
7264 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7265
7266 SetDefaultStyle(newStyle);
7267
5d7836c4
JS
7268 return true;
7269}
7270
7271/// End the style
7272bool wxRichTextBuffer::EndStyle()
7273{
63886f6d 7274 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7275 {
7276 wxLogDebug(_("Too many EndStyle calls!"));
7277 return false;
7278 }
7279
09f14108 7280 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7281 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7282 m_attributeStack.Erase(node);
5d7836c4
JS
7283
7284 SetDefaultStyle(*attr);
7285
7286 delete attr;
7287 return true;
7288}
7289
7290/// End all styles
7291bool wxRichTextBuffer::EndAllStyles()
7292{
7293 while (m_attributeStack.GetCount() != 0)
7294 EndStyle();
7295 return true;
7296}
7297
7298/// Clear the style stack
7299void wxRichTextBuffer::ClearStyleStack()
7300{
09f14108 7301 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7302 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7303 m_attributeStack.Clear();
7304}
7305
7306/// Begin using bold
7307bool wxRichTextBuffer::BeginBold()
7308{
24777478 7309 wxRichTextAttr attr;
7d76fbd5 7310 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7311
5d7836c4
JS
7312 return BeginStyle(attr);
7313}
7314
7315/// Begin using italic
7316bool wxRichTextBuffer::BeginItalic()
7317{
24777478 7318 wxRichTextAttr attr;
7d76fbd5 7319 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7320
5d7836c4
JS
7321 return BeginStyle(attr);
7322}
7323
7324/// Begin using underline
7325bool wxRichTextBuffer::BeginUnderline()
7326{
24777478 7327 wxRichTextAttr attr;
44cc96a8 7328 attr.SetFontUnderlined(true);
7fe8059f 7329
5d7836c4
JS
7330 return BeginStyle(attr);
7331}
7332
7333/// Begin using point size
7334bool wxRichTextBuffer::BeginFontSize(int pointSize)
7335{
24777478 7336 wxRichTextAttr attr;
44cc96a8 7337 attr.SetFontSize(pointSize);
7fe8059f 7338
5d7836c4
JS
7339 return BeginStyle(attr);
7340}
7341
7342/// Begin using this font
7343bool wxRichTextBuffer::BeginFont(const wxFont& font)
7344{
24777478 7345 wxRichTextAttr attr;
5d7836c4 7346 attr.SetFont(font);
7fe8059f 7347
5d7836c4
JS
7348 return BeginStyle(attr);
7349}
7350
7351/// Begin using this colour
7352bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7353{
24777478 7354 wxRichTextAttr attr;
5d7836c4
JS
7355 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7356 attr.SetTextColour(colour);
7fe8059f 7357
5d7836c4
JS
7358 return BeginStyle(attr);
7359}
7360
7361/// Begin using alignment
7362bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7363{
24777478 7364 wxRichTextAttr attr;
5d7836c4
JS
7365 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7366 attr.SetAlignment(alignment);
7fe8059f 7367
5d7836c4
JS
7368 return BeginStyle(attr);
7369}
7370
7371/// Begin left indent
7372bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7373{
24777478 7374 wxRichTextAttr attr;
5d7836c4
JS
7375 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7376 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7377
5d7836c4
JS
7378 return BeginStyle(attr);
7379}
7380
7381/// Begin right indent
7382bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7383{
24777478 7384 wxRichTextAttr attr;
5d7836c4
JS
7385 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7386 attr.SetRightIndent(rightIndent);
7fe8059f 7387
5d7836c4
JS
7388 return BeginStyle(attr);
7389}
7390
7391/// Begin paragraph spacing
7392bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7393{
7394 long flags = 0;
7395 if (before != 0)
7396 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7397 if (after != 0)
7398 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7399
24777478 7400 wxRichTextAttr attr;
5d7836c4
JS
7401 attr.SetFlags(flags);
7402 attr.SetParagraphSpacingBefore(before);
7403 attr.SetParagraphSpacingAfter(after);
7fe8059f 7404
5d7836c4
JS
7405 return BeginStyle(attr);
7406}
7407
7408/// Begin line spacing
7409bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
7410{
24777478 7411 wxRichTextAttr attr;
5d7836c4
JS
7412 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
7413 attr.SetLineSpacing(lineSpacing);
7fe8059f 7414
5d7836c4
JS
7415 return BeginStyle(attr);
7416}
7417
7418/// Begin numbered bullet
7419bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
7420{
24777478 7421 wxRichTextAttr attr;
f089713f 7422 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7423 attr.SetBulletStyle(bulletStyle);
7424 attr.SetBulletNumber(bulletNumber);
7425 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7426
5d7836c4
JS
7427 return BeginStyle(attr);
7428}
7429
7430/// Begin symbol bullet
d2d0adc7 7431bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 7432{
24777478 7433 wxRichTextAttr attr;
f089713f 7434 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7435 attr.SetBulletStyle(bulletStyle);
7436 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 7437 attr.SetBulletText(symbol);
7fe8059f 7438
5d7836c4
JS
7439 return BeginStyle(attr);
7440}
7441
f089713f
JS
7442/// Begin standard bullet
7443bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
7444{
24777478 7445 wxRichTextAttr attr;
f089713f
JS
7446 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7447 attr.SetBulletStyle(bulletStyle);
7448 attr.SetLeftIndent(leftIndent, leftSubIndent);
7449 attr.SetBulletName(bulletName);
7450
7451 return BeginStyle(attr);
7452}
7453
5d7836c4
JS
7454/// Begin named character style
7455bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
7456{
7457 if (GetStyleSheet())
7458 {
7459 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7460 if (def)
7461 {
24777478 7462 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7463 return BeginStyle(attr);
7464 }
7465 }
7466 return false;
7467}
7468
7469/// Begin named paragraph style
7470bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
7471{
7472 if (GetStyleSheet())
7473 {
7474 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
7475 if (def)
7476 {
24777478 7477 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7478 return BeginStyle(attr);
7479 }
7480 }
7481 return false;
7482}
7483
f089713f
JS
7484/// Begin named list style
7485bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
7486{
7487 if (GetStyleSheet())
7488 {
7489 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
7490 if (def)
7491 {
24777478 7492 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
7493
7494 attr.SetBulletNumber(number);
7495
7496 return BeginStyle(attr);
7497 }
7498 }
7499 return false;
7500}
7501
d2d0adc7
JS
7502/// Begin URL
7503bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
7504{
24777478 7505 wxRichTextAttr attr;
d2d0adc7
JS
7506
7507 if (!characterStyle.IsEmpty() && GetStyleSheet())
7508 {
7509 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7510 if (def)
7511 {
336d8ae9 7512 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
7513 }
7514 }
7515 attr.SetURL(url);
7516
7517 return BeginStyle(attr);
7518}
7519
5d7836c4
JS
7520/// Adds a handler to the end
7521void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
7522{
7523 sm_handlers.Append(handler);
7524}
7525
7526/// Inserts a handler at the front
7527void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
7528{
7529 sm_handlers.Insert( handler );
7530}
7531
7532/// Removes a handler
7533bool wxRichTextBuffer::RemoveHandler(const wxString& name)
7534{
7535 wxRichTextFileHandler *handler = FindHandler(name);
7536 if (handler)
7537 {
7538 sm_handlers.DeleteObject(handler);
7539 delete handler;
7540 return true;
7541 }
7542 else
7543 return false;
7544}
7545
7546/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
7547wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
7548 wxRichTextFileType imageType)
5d7836c4
JS
7549{
7550 if (imageType != wxRICHTEXT_TYPE_ANY)
7551 return FindHandler(imageType);
0ca07313 7552 else if (!filename.IsEmpty())
5d7836c4
JS
7553 {
7554 wxString path, file, ext;
a51e601e 7555 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
7556 return FindHandler(ext, imageType);
7557 }
0ca07313
JS
7558 else
7559 return NULL;
5d7836c4
JS
7560}
7561
7562
7563/// Finds a handler by name
7564wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
7565{
7566 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7567 while (node)
7568 {
7569 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7570 if (handler->GetName().Lower() == name.Lower()) return handler;
7571
7572 node = node->GetNext();
7573 }
7574 return NULL;
7575}
7576
7577/// Finds a handler by extension and type
d75a69e8 7578wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
7579{
7580 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7581 while (node)
7582 {
7583 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7584 if ( handler->GetExtension().Lower() == extension.Lower() &&
7585 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
7586 return handler;
7587 node = node->GetNext();
7588 }
7589 return 0;
7590}
7591
7592/// Finds a handler by type
d75a69e8 7593wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
7594{
7595 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7596 while (node)
7597 {
7598 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
7599 if (handler->GetType() == type) return handler;
7600 node = node->GetNext();
7601 }
7602 return NULL;
7603}
7604
7605void wxRichTextBuffer::InitStandardHandlers()
7606{
7607 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
7608 AddHandler(new wxRichTextPlainTextHandler);
7609}
7610
7611void wxRichTextBuffer::CleanUpHandlers()
7612{
7613 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7614 while (node)
7615 {
7616 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
7617 wxList::compatibility_iterator next = node->GetNext();
7618 delete handler;
7619 node = next;
7620 }
7621
7622 sm_handlers.Clear();
7623}
7624
1e967276 7625wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 7626{
1e967276
JS
7627 if (types)
7628 types->Clear();
7629
5d7836c4
JS
7630 wxString wildcard;
7631
7632 wxList::compatibility_iterator node = GetHandlers().GetFirst();
7633 int count = 0;
7634 while (node)
7635 {
7636 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 7637 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
7638 {
7639 if (combine)
7640 {
7641 if (count > 0)
7642 wildcard += wxT(";");
7643 wildcard += wxT("*.") + handler->GetExtension();
7644 }
7645 else
7646 {
7647 if (count > 0)
7648 wildcard += wxT("|");
7649 wildcard += handler->GetName();
7650 wildcard += wxT(" ");
7651 wildcard += _("files");
7652 wildcard += wxT(" (*.");
7653 wildcard += handler->GetExtension();
7654 wildcard += wxT(")|*.");
7655 wildcard += handler->GetExtension();
1e967276
JS
7656 if (types)
7657 types->Add(handler->GetType());
5d7836c4
JS
7658 }
7659 count ++;
7660 }
7661
7662 node = node->GetNext();
7663 }
7664
7665 if (combine)
7666 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
7667 return wildcard;
7668}
7669
7670/// Load a file
d75a69e8 7671bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7672{
7673 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7674 if (handler)
1e967276 7675 {
24777478 7676 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7677 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7678 bool success = handler->LoadFile(this, filename);
7679 Invalidate(wxRICHTEXT_ALL);
7680 return success;
7681 }
5d7836c4
JS
7682 else
7683 return false;
7684}
7685
7686/// Save a file
d75a69e8 7687bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7688{
7689 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7690 if (handler)
d2d0adc7
JS
7691 {
7692 handler->SetFlags(GetHandlerFlags());
5d7836c4 7693 return handler->SaveFile(this, filename);
d2d0adc7 7694 }
5d7836c4
JS
7695 else
7696 return false;
7697}
7698
7699/// Load from a stream
d75a69e8 7700bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7701{
7702 wxRichTextFileHandler* handler = FindHandler(type);
7703 if (handler)
1e967276 7704 {
24777478 7705 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7706 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7707 bool success = handler->LoadFile(this, stream);
7708 Invalidate(wxRICHTEXT_ALL);
7709 return success;
7710 }
5d7836c4
JS
7711 else
7712 return false;
7713}
7714
7715/// Save to a stream
d75a69e8 7716bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7717{
7718 wxRichTextFileHandler* handler = FindHandler(type);
7719 if (handler)
d2d0adc7
JS
7720 {
7721 handler->SetFlags(GetHandlerFlags());
5d7836c4 7722 return handler->SaveFile(this, stream);
d2d0adc7 7723 }
5d7836c4
JS
7724 else
7725 return false;
7726}
7727
7728/// Copy the range to the clipboard
7729bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
7730{
7731 bool success = false;
603f702b
JS
7732 wxRichTextParagraphLayoutBox* container = this;
7733 if (GetRichTextCtrl())
7734 container = GetRichTextCtrl()->GetFocusObject();
7735
11ef729d 7736#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 7737
d2142335 7738 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 7739 {
0ca07313
JS
7740 wxTheClipboard->Clear();
7741
7742 // Add composite object
7743
7744 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
7745
7746 {
603f702b 7747 wxString text = container->GetTextForRange(range);
0ca07313
JS
7748
7749#ifdef __WXMSW__
7750 text = wxTextFile::Translate(text, wxTextFileType_Dos);
7751#endif
7752
7753 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
7754 }
7755
7756 // Add rich text buffer data object. This needs the XML handler to be present.
7757
7758 if (FindHandler(wxRICHTEXT_TYPE_XML))
7759 {
7760 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 7761 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
7762
7763 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
7764 }
7765
7766 if (wxTheClipboard->SetData(compositeObject))
7767 success = true;
7768
5d7836c4
JS
7769 wxTheClipboard->Close();
7770 }
0ca07313 7771
39a1c2f2
WS
7772#else
7773 wxUnusedVar(range);
7774#endif
5d7836c4
JS
7775 return success;
7776}
7777
7778/// Paste the clipboard content to the buffer
7779bool wxRichTextBuffer::PasteFromClipboard(long position)
7780{
7781 bool success = false;
603f702b
JS
7782 wxRichTextParagraphLayoutBox* container = this;
7783 if (GetRichTextCtrl())
7784 container = GetRichTextCtrl()->GetFocusObject();
7785
11ef729d 7786#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
7787 if (CanPasteFromClipboard())
7788 {
7789 if (wxTheClipboard->Open())
7790 {
0ca07313
JS
7791 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7792 {
7793 wxRichTextBufferDataObject data;
7794 wxTheClipboard->GetData(data);
7795 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
7796 if (richTextBuffer)
7797 {
4e63bfb9 7798 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 7799 if (GetRichTextCtrl())
603f702b 7800 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
7801 delete richTextBuffer;
7802 }
7803 }
f7d83f24 7804 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
7805 #if wxUSE_UNICODE
7806 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
7807 #endif
f7d83f24 7808 )
5d7836c4
JS
7809 {
7810 wxTextDataObject data;
7811 wxTheClipboard->GetData(data);
7812 wxString text(data.GetText());
c21f3211
JS
7813#ifdef __WXMSW__
7814 wxString text2;
7815 text2.Alloc(text.Length()+1);
7816 size_t i;
7817 for (i = 0; i < text.Length(); i++)
7818 {
7819 wxChar ch = text[i];
7820 if (ch != wxT('\r'))
7821 text2 += ch;
7822 }
7823#else
7824 wxString text2 = text;
7825#endif
4e63bfb9 7826 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 7827
62381daa
JS
7828 if (GetRichTextCtrl())
7829 GetRichTextCtrl()->ShowPosition(position + text2.Length());
7830
5d7836c4
JS
7831 success = true;
7832 }
7833 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
7834 {
7835 wxBitmapDataObject data;
7836 wxTheClipboard->GetData(data);
7837 wxBitmap bitmap(data.GetBitmap());
7838 wxImage image(bitmap.ConvertToImage());
7839
603f702b 7840 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 7841
5d7836c4
JS
7842 action->GetNewParagraphs().AddImage(image);
7843
7844 if (action->GetNewParagraphs().GetChildCount() == 1)
7845 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 7846
9c8e10ad 7847 action->SetPosition(position+1);
7fe8059f 7848
5d7836c4 7849 // Set the range we'll need to delete in Undo
9c8e10ad 7850 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 7851
5d7836c4
JS
7852 SubmitAction(action);
7853
7854 success = true;
7855 }
7856 wxTheClipboard->Close();
7857 }
7858 }
39a1c2f2
WS
7859#else
7860 wxUnusedVar(position);
7861#endif
5d7836c4
JS
7862 return success;
7863}
7864
7865/// Can we paste from the clipboard?
7866bool wxRichTextBuffer::CanPasteFromClipboard() const
7867{
7fe8059f 7868 bool canPaste = false;
11ef729d 7869#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 7870 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 7871 {
f7d83f24
VZ
7872 if (wxTheClipboard->IsSupported(wxDF_TEXT)
7873#if wxUSE_UNICODE
603f702b
JS
7874 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
7875#endif
7876 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7877 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 7878 {
7fe8059f 7879 canPaste = true;
5d7836c4
JS
7880 }
7881 wxTheClipboard->Close();
7882 }
39a1c2f2 7883#endif
5d7836c4
JS
7884 return canPaste;
7885}
7886
7887/// Dumps contents of buffer for debugging purposes
7888void wxRichTextBuffer::Dump()
7889{
7890 wxString text;
7891 {
7892 wxStringOutputStream stream(& text);
7893 wxTextOutputStream textStream(stream);
7894 Dump(textStream);
7895 }
7896
7897 wxLogDebug(text);
7898}
7899
d2d0adc7
JS
7900/// Add an event handler
7901bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
7902{
7903 m_eventHandlers.Append(handler);
7904 return true;
7905}
7906
7907/// Remove an event handler
7908bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
7909{
7910 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
7911 if (node)
7912 {
7913 m_eventHandlers.Erase(node);
7914 if (deleteHandler)
7915 delete handler;
3e541562 7916
d2d0adc7
JS
7917 return true;
7918 }
7919 else
7920 return false;
7921}
7922
7923/// Clear event handlers
7924void wxRichTextBuffer::ClearEventHandlers()
7925{
7926 m_eventHandlers.Clear();
7927}
7928
7929/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7930/// otherwise will stop at the first successful one.
7931bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
7932{
7933 bool success = false;
7934 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
7935 {
7936 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
7937 if (handler->ProcessEvent(event))
7938 {
7939 success = true;
7940 if (!sendToAll)
7941 return true;
7942 }
7943 }
7944 return success;
7945}
7946
7947/// Set style sheet and notify of the change
7948bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
7949{
7950 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 7951
616c7cbd 7952 wxWindowID winid = wxID_ANY;
d2d0adc7 7953 if (GetRichTextCtrl())
616c7cbd 7954 winid = GetRichTextCtrl()->GetId();
3e541562 7955
616c7cbd 7956 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 7957 event.SetEventObject(GetRichTextCtrl());
603f702b 7958 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
7959 event.SetOldStyleSheet(oldSheet);
7960 event.SetNewStyleSheet(sheet);
7961 event.Allow();
3e541562 7962
d2d0adc7
JS
7963 if (SendEvent(event) && !event.IsAllowed())
7964 {
7965 if (sheet != oldSheet)
7966 delete sheet;
7967
7968 return false;
7969 }
7970
7971 if (oldSheet && oldSheet != sheet)
7972 delete oldSheet;
7973
7974 SetStyleSheet(sheet);
7975
7976 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
7977 event.SetOldStyleSheet(NULL);
7978 event.Allow();
7979
7980 return SendEvent(event);
7981}
7982
7983/// Set renderer, deleting old one
7984void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
7985{
7986 if (sm_renderer)
7987 delete sm_renderer;
7988 sm_renderer = renderer;
7989}
7990
603f702b
JS
7991/// Hit-testing: returns a flag indicating hit test details, plus
7992/// information about position
8db2e3ef 7993int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 7994{
8db2e3ef 7995 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
7996 if (ret != wxRICHTEXT_HITTEST_NONE)
7997 {
7998 return ret;
7999 }
8000 else
8001 {
8002 textPosition = m_ownRange.GetEnd()-1;
8003 *obj = this;
8004 *contextObj = this;
8005 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8006 }
8007}
8008
24777478 8009bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8010{
a1b806b9 8011 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8012 {
ecb5fbf1
JS
8013 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8014 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8015 }
8016 else
8017 {
ecb5fbf1
JS
8018 wxCheckSetPen(dc, *wxBLACK_PEN);
8019 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8020 }
8021
8022 wxFont font;
44cc96a8
JS
8023 if (bulletAttr.HasFont())
8024 {
8025 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8026 }
d2d0adc7
JS
8027 else
8028 font = (*wxNORMAL_FONT);
8029
ecb5fbf1 8030 wxCheckSetFont(dc, font);
d2d0adc7
JS
8031
8032 int charHeight = dc.GetCharHeight();
3e541562 8033
d2d0adc7
JS
8034 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8035 int bulletHeight = bulletWidth;
8036
8037 int x = rect.x;
3e541562 8038
d2d0adc7
JS
8039 // Calculate the top position of the character (as opposed to the whole line height)
8040 int y = rect.y + (rect.height - charHeight);
3e541562 8041
d2d0adc7
JS
8042 // Calculate where the bullet should be positioned
8043 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8044
d2d0adc7 8045 // The margin between a bullet and text.
44219ff0 8046 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8047
d2d0adc7
JS
8048 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8049 x = rect.x + rect.width - bulletWidth - margin;
8050 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8051 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8052
d2d0adc7
JS
8053 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8054 {
8055 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8056 }
8057 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8058 {
8059 wxPoint pts[5];
8060 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8061 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8062 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8063 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8064
d2d0adc7
JS
8065 dc.DrawPolygon(4, pts);
8066 }
8067 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8068 {
8069 wxPoint pts[3];
8070 pts[0].x = x; pts[0].y = y;
8071 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8072 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8073
d2d0adc7
JS
8074 dc.DrawPolygon(3, pts);
8075 }
603f702b
JS
8076 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8077 {
8078 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8079 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8080 }
d2d0adc7
JS
8081 else // "standard/circle", and catch-all
8082 {
8083 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8084 }
8085
d2d0adc7
JS
8086 return true;
8087}
8088
24777478 8089bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8090{
8091 if (!text.empty())
8092 {
8093 wxFont font;
44cc96a8
JS
8094 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8095 {
24777478 8096 wxRichTextAttr fontAttr;
44cc96a8
JS
8097 fontAttr.SetFontSize(attr.GetFontSize());
8098 fontAttr.SetFontStyle(attr.GetFontStyle());
8099 fontAttr.SetFontWeight(attr.GetFontWeight());
8100 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8101 fontAttr.SetFontFaceName(attr.GetBulletFont());
8102 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8103 }
8104 else if (attr.HasFont())
8105 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8106 else
8107 font = (*wxNORMAL_FONT);
8108
ecb5fbf1 8109 wxCheckSetFont(dc, font);
d2d0adc7 8110
a1b806b9 8111 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8112 dc.SetTextForeground(attr.GetTextColour());
8113
04ee05f9 8114 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8115
8116 int charHeight = dc.GetCharHeight();
8117 wxCoord tw, th;
8118 dc.GetTextExtent(text, & tw, & th);
8119
8120 int x = rect.x;
8121
8122 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8123 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8124
8125 // The margin between a bullet and text.
44219ff0 8126 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8127
d2d0adc7
JS
8128 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8129 x = (rect.x + rect.width) - tw - margin;
8130 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8131 x = x + (rect.width)/2 - tw/2;
8132
8133 dc.DrawText(text, x, y);
3e541562 8134
d2d0adc7
JS
8135 return true;
8136 }
8137 else
8138 return false;
8139}
8140
24777478 8141bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8142{
8143 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8144 // with the buffer. The store will allow retrieval from memory, disk or other means.
8145 return false;
8146}
8147
8148/// Enumerate the standard bullet names currently supported
8149bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8150{
04529b2a 8151 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8152 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8153 bulletNames.Add(wxTRANSLATE("standard/square"));
8154 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8155 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8156
8157 return true;
8158}
5d7836c4 8159
bec80f4f
JS
8160/*!
8161 * wxRichTextBox
8162 */
8163
603f702b 8164IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8165
8166wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8167 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8168{
8169}
8170
8171/// Draw the item
8db2e3ef 8172bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8173{
603f702b
JS
8174 if (!IsShown())
8175 return true;
5ad9ae3a 8176
603f702b
JS
8177 // TODO: if the active object in the control, draw an indication.
8178 // We need to add the concept of active object, and not just focus object,
8179 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8180 // Ultimately we would like to be able to interactively resize an active object
8181 // using drag handles.
8db2e3ef 8182 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8183}
5ad9ae3a 8184
603f702b
JS
8185/// Copy
8186void wxRichTextBox::Copy(const wxRichTextBox& obj)
8187{
8188 wxRichTextParagraphLayoutBox::Copy(obj);
8189}
8190
8191// Edit properties via a GUI
8192bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8193{
8194 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8195 boxDlg.SetAttributes(GetAttributes());
8196
8197 if (boxDlg.ShowModal() == wxID_OK)
8198 {
8199 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8200 // indeterminate in the object.
8201 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8202 return true;
5ad9ae3a 8203 }
603f702b
JS
8204 else
8205 return false;
bec80f4f
JS
8206}
8207
603f702b
JS
8208IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
8209
8210wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
8211 wxRichTextBox(parent)
bec80f4f 8212{
603f702b
JS
8213}
8214
8215/// Draw the item
8db2e3ef 8216bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 8217{
8db2e3ef 8218 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
8219}
8220
8221/// Copy
8222void wxRichTextCell::Copy(const wxRichTextCell& obj)
8223{
8224 wxRichTextBox::Copy(obj);
8225}
8226
8227// Edit properties via a GUI
8228bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8229{
8230 // We need to gather common attributes for all selected cells.
8231
8232 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
8233 bool multipleCells = false;
8234 wxRichTextAttr attr;
8235
8236 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
8237 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 8238 {
603f702b
JS
8239 wxRichTextAttr clashingAttr, absentAttr;
8240 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8241 size_t i;
8242 int selectedCellCount = 0;
8243 for (i = 0; i < sel.GetCount(); i++)
8244 {
8245 const wxRichTextRange& range = sel[i];
8246 wxRichTextCell* cell = table->GetCell(range.GetStart());
8247 if (cell)
8248 {
8249 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 8250
603f702b
JS
8251 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
8252
8253 selectedCellCount ++;
8254 }
8255 }
8256 multipleCells = selectedCellCount > 1;
5ad9ae3a 8257 }
603f702b
JS
8258 else
8259 {
8260 attr = GetAttributes();
8261 }
8262
8263 wxString caption;
8264 if (multipleCells)
8265 caption = _("Multiple Cell Properties");
8266 else
8267 caption = _("Cell Properties");
8268
8269 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
8270 cellDlg.SetAttributes(attr);
8271
8272 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
8273 if (sizePage)
8274 {
8275 // We don't want position and floating controls for a cell.
8276 sizePage->ShowPositionControls(false);
8277 sizePage->ShowFloatingControls(false);
8278 }
8279
8280 if (cellDlg.ShowModal() == wxID_OK)
8281 {
8282 if (multipleCells)
8283 {
8284 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8285 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8286 // since it may represent clashing attributes across multiple objects.
8287 table->SetCellStyle(sel, attr);
8288 }
8289 else
8290 // For a single object, indeterminate attributes set by the user should be reflected in the
8291 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8292 // the style directly instead of applying (which ignores indeterminate attributes,
8293 // leaving them as they were).
8294 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8295 return true;
8296 }
8297 else
8298 return false;
8299}
8300
8301WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
8302
8303IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
8304
8305wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
8306{
8307 m_rowCount = 0;
8308 m_colCount = 0;
8309}
5ad9ae3a 8310
603f702b 8311// Draws the object.
8db2e3ef 8312bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 8313{
8db2e3ef 8314 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
8315}
8316
603f702b
JS
8317WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
8318WX_DEFINE_OBJARRAY(wxRichTextRectArray);
8319
8320// Lays the object out. rect is the space available for layout. Often it will
8321// be the specified overall space for this object, if trying to constrain
8322// layout to a particular size, or it could be the total space available in the
8323// parent. rect is the overall size, so we must subtract margins and padding.
8324// to get the actual available space.
8db2e3ef 8325bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 8326{
603f702b
JS
8327 SetPosition(rect.GetPosition());
8328
8329 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8330 // minimum size if within alloted size, then divide up remaining size
8331 // between rows/cols.
8332
8333 double scale = 1.0;
8334 wxRichTextBuffer* buffer = GetBuffer();
8335 if (buffer) scale = buffer->GetScale();
8336
8db2e3ef 8337 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
8338 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
8339
8db2e3ef
JS
8340 wxRichTextAttr attr(GetAttributes());
8341 context.ApplyVirtualAttributes(attr, this);
8342
603f702b
JS
8343 // If we have no fixed table size, and assuming we're not pushed for
8344 // space, then we don't have to try to stretch the table to fit the contents.
8345 bool stretchToFitTableWidth = false;
8346
8347 int tableWidth = rect.width;
8db2e3ef 8348 if (attr.GetTextBoxAttr().GetWidth().IsValid())
603f702b 8349 {
8db2e3ef 8350 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
8351
8352 // Fixed table width, so we do want to stretch columns out if necessary.
8353 stretchToFitTableWidth = true;
8354
8355 // Shouldn't be able to exceed the size passed to this function
8356 tableWidth = wxMin(rect.width, tableWidth);
8357 }
8358
8359 // Get internal padding
36307fdf 8360 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
8361 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8362 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
8363 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8364 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
8365
8366 // Assume that left and top padding are also used for inter-cell padding.
8367 int paddingX = paddingLeft;
8368 int paddingY = paddingTop;
8369
8370 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 8371 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
8372
8373 // Internal table width - the area for content
8374 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
8375
8376 int rowCount = m_cells.GetCount();
8377 if (m_colCount == 0 || rowCount == 0)
8378 {
8379 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
8380 SetCachedSize(overallRect.GetSize());
8381
8382 // Zero content size
8383 SetMinSize(overallRect.GetSize());
8384 SetMaxSize(GetMinSize());
8385 return true;
8386 }
8387
8388 // The final calculated widths
bb7bbd12
JS
8389 wxArrayInt colWidths;
8390 colWidths.Add(0, m_colCount);
603f702b 8391
bb7bbd12
JS
8392 wxArrayInt absoluteColWidths;
8393 absoluteColWidths.Add(0, m_colCount);
603f702b 8394 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
bb7bbd12
JS
8395 wxArrayInt percentageColWidths;
8396 percentageColWidths.Add(0, m_colCount);
603f702b
JS
8397 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8398 // These are only relevant when the first column contains spanning information.
8399 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
8400 wxArrayInt maxColWidths;
8401 maxColWidths.Add(0, m_colCount);
8402 wxArrayInt minColWidths;
8403 minColWidths.Add(0, m_colCount);
603f702b
JS
8404
8405 wxSize tableSize(tableWidth, 0);
8406
8407 int i, j, k;
8408
8409 for (i = 0; i < m_colCount; i++)
8410 {
8411 absoluteColWidths[i] = 0;
8412 // absoluteColWidthsSpanning[i] = 0;
8413 percentageColWidths[i] = -1;
8414 // percentageColWidthsSpanning[i] = -1;
8415 colWidths[i] = 0;
8416 maxColWidths[i] = 0;
8417 minColWidths[i] = 0;
8418 // columnSpans[i] = 1;
8419 }
8420
8421 // (0) Determine which cells are visible according to spans
8422 // 1 2 3 4 5
8423 // __________________
8424 // | | | | | 1
8425 // |------| |----|
8426 // |------| | | 2
8427 // |------| | | 3
8428 // |------------------|
8429 // |__________________| 4
8430
8431 // To calculate cell visibility:
8432 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8433 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8434 // that cell, hide the cell.
8435
8436 // We can also use this array to match the size of spanning cells to the grid. Or just do
8437 // this when we iterate through all cells.
8438
8439 // 0.1: add spanning cells to an array
8440 wxRichTextRectArray rectArray;
8441 for (j = 0; j < m_rowCount; j++)
8442 {
8443 for (i = 0; i < m_colCount; i++)
8444 {
8445 wxRichTextBox* cell = GetCell(j, i);
8446 int colSpan = 1, rowSpan = 1;
8447 if (cell->GetProperties().HasProperty(wxT("colspan")))
8448 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8449 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8450 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8451 if (colSpan > 1 || rowSpan > 1)
8452 {
8453 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
8454 }
8455 }
8456 }
8457 // 0.2: find which cells are subsumed by a spanning cell
8458 for (j = 0; j < m_rowCount; j++)
8459 {
8460 for (i = 0; i < m_colCount; i++)
8461 {
8462 wxRichTextBox* cell = GetCell(j, i);
8463 if (rectArray.GetCount() == 0)
8464 {
8465 cell->Show(true);
8466 }
8467 else
8468 {
8469 int colSpan = 1, rowSpan = 1;
8470 if (cell->GetProperties().HasProperty(wxT("colspan")))
8471 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8472 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8473 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8474 if (colSpan > 1 || rowSpan > 1)
8475 {
8476 // Assume all spanning cells are shown
8477 cell->Show(true);
8478 }
8479 else
8480 {
8481 bool shown = true;
8482 for (k = 0; k < (int) rectArray.GetCount(); k++)
8483 {
8484 if (rectArray[k].Contains(wxPoint(i, j)))
8485 {
8486 shown = false;
8487 break;
8488 }
8489 }
8490 cell->Show(shown);
8491 }
8492 }
8493 }
8494 }
8495
8496 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8497 // overlap with a spanned cell starting at a previous column position.
8498 // This means we need to keep an array of rects so we can check. However
8499 // it does also mean that some spans simply may not be taken into account
8500 // where there are different spans happening on different rows. In these cases,
8501 // they will simply be as wide as their constituent columns.
8502
8503 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8504 // the absolute or percentage width of each column.
8505
8506 for (j = 0; j < m_rowCount; j++)
8507 {
8508 // First get the overall margins so we can calculate percentage widths based on
8509 // the available content space for all cells on the row
8510
8511 int overallRowContentMargin = 0;
8512 int visibleCellCount = 0;
8513
8514 for (i = 0; i < m_colCount; i++)
8515 {
8516 wxRichTextBox* cell = GetCell(j, i);
8517 if (cell->IsShown())
8518 {
8519 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
8520 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
8521
8522 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
8523 visibleCellCount ++;
8524 }
8525 }
8526
8527 // Add in inter-cell padding
8528 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
8529
8530 int rowContentWidth = internalTableWidth - overallRowContentMargin;
8531 wxSize rowTableSize(rowContentWidth, 0);
8532 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
8533
8534 for (i = 0; i < m_colCount; i++)
8535 {
8536 wxRichTextBox* cell = GetCell(j, i);
8537 if (cell->IsShown())
8538 {
8539 int colSpan = 1;
8540 if (cell->GetProperties().HasProperty(wxT("colspan")))
8541 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8542
8543 // Lay out cell to find min/max widths
8544 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 8545 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
8546
8547 if (colSpan == 1)
8548 {
8549 int absoluteCellWidth = -1;
8550 int percentageCellWidth = -1;
8551
8552 // I think we need to calculate percentages from the internal table size,
8553 // minus the padding between cells which we'll need to calculate from the
8554 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8555 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8556 // so if we want to conform to that we'll need to add in the overall cell margins.
8557 // However, this will make it difficult to specify percentages that add up to
8558 // 100% and still fit within the table width.
8559 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8560 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8561 // If we're using internal content size for the width, we would calculate the
8562 // the overall cell width for n cells as:
8563 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8564 // + thisOverallCellMargin
8565 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8566 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8567
8568 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8569 {
8570 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
8571 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
8572 {
8573 percentageCellWidth = w;
8574 }
8575 else
8576 {
8577 absoluteCellWidth = w;
8578 }
8579 // Override absolute width with minimum width if necessary
8580 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
8581 absoluteCellWidth = cell->GetMinSize().x;
8582 }
8583
8584 if (absoluteCellWidth != -1)
8585 {
8586 if (absoluteCellWidth > absoluteColWidths[i])
8587 absoluteColWidths[i] = absoluteCellWidth;
8588 }
8589
8590 if (percentageCellWidth != -1)
8591 {
8592 if (percentageCellWidth > percentageColWidths[i])
8593 percentageColWidths[i] = percentageCellWidth;
8594 }
8595
8596 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
8597 minColWidths[i] = cell->GetMinSize().x;
8598 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
8599 maxColWidths[i] = cell->GetMaxSize().x;
8600 }
8601 }
8602 }
8603 }
8604
8605 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8606 // TODO: simply merge this into (1).
8607 for (i = 0; i < m_colCount; i++)
8608 {
8609 if (absoluteColWidths[i] > 0)
8610 {
8611 colWidths[i] = absoluteColWidths[i];
8612 }
8613 else if (percentageColWidths[i] > 0)
8614 {
8615 colWidths[i] = percentageColWidths[i];
8616
8617 // This is rubbish - we calculated the absolute widths from percentages, so
8618 // we can't do it again here.
8619 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8620 }
8621 }
8622
8623 // (3) Process absolute or proportional widths of spanning columns,
8624 // now that we know what our fixed column widths are going to be.
8625 // Spanned cells will try to adjust columns so the span will fit.
8626 // Even existing fixed column widths can be expanded if necessary.
8627 // Actually, currently fixed columns widths aren't adjusted; instead,
8628 // the algorithm favours earlier rows and adjusts unspecified column widths
8629 // the first time only. After that, we can't know whether the column has been
8630 // specified explicitly or not. (We could make a note if necessary.)
8631 for (j = 0; j < m_rowCount; j++)
8632 {
8633 // First get the overall margins so we can calculate percentage widths based on
8634 // the available content space for all cells on the row
8635
8636 int overallRowContentMargin = 0;
8637 int visibleCellCount = 0;
8638
8639 for (i = 0; i < m_colCount; i++)
8640 {
8641 wxRichTextBox* cell = GetCell(j, i);
8642 if (cell->IsShown())
8643 {
8644 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
8645 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
8646
8647 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
8648 visibleCellCount ++;
8649 }
8650 }
8651
8652 // Add in inter-cell padding
8653 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
8654
8655 int rowContentWidth = internalTableWidth - overallRowContentMargin;
8656 wxSize rowTableSize(rowContentWidth, 0);
8657 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
8658
8659 for (i = 0; i < m_colCount; i++)
8660 {
8661 wxRichTextBox* cell = GetCell(j, i);
8662 if (cell->IsShown())
8663 {
8664 int colSpan = 1;
8665 if (cell->GetProperties().HasProperty(wxT("colspan")))
8666 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8667
8668 if (colSpan > 1)
8669 {
8670 int spans = wxMin(colSpan, m_colCount - i);
8671 int cellWidth = 0;
8672 if (spans > 0)
8673 {
8674 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8675 {
8676 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
8677 // Override absolute width with minimum width if necessary
8678 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
8679 cellWidth = cell->GetMinSize().x;
8680 }
8681 else
8682 {
8683 // Do we want to do this? It's the only chance we get to
8684 // use the cell's min/max sizes, so we need to work out
8685 // how we're going to balance the unspecified spanning cell
8686 // width with the possibility more-constrained constituent cell widths.
8687 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8688 // don't want to constraint all the spanned columns to fit into this cell.
8689 // OK, let's say that if any of the constituent columns don't fit,
8690 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8691 // cells to the columns later.
8692 cellWidth = cell->GetMinSize().x;
8693 if (cell->GetMaxSize().x > cellWidth)
8694 cellWidth = cell->GetMaxSize().x;
8695 }
8696
8697 // Subtract the padding between cells
8698 int spanningWidth = cellWidth;
8699 spanningWidth -= paddingX * (spans-1);
8700
8701 if (spanningWidth > 0)
8702 {
8703 // Now share the spanning width between columns within that span
8704 // TODO: take into account min widths of columns within the span
8705 int spanningWidthLeft = spanningWidth;
8706 int stretchColCount = 0;
8707 for (k = i; k < (i+spans); k++)
8708 {
8709 if (colWidths[k] > 0) // absolute or proportional width has been specified
8710 spanningWidthLeft -= colWidths[k];
8711 else
8712 stretchColCount ++;
8713 }
8714 // Now divide what's left between the remaining columns
8715 int colShare = 0;
8716 if (stretchColCount > 0)
8717 colShare = spanningWidthLeft / stretchColCount;
8718 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
8719
8720 // If fixed-width columns are currently too big, then we'll later
8721 // stretch the spanned cell to fit.
8722
8723 if (spanningWidthLeft > 0)
8724 {
8725 for (k = i; k < (i+spans); k++)
8726 {
8727 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
8728 {
8729 int newWidth = colShare;
8730 if (k == (i+spans-1))
8731 newWidth += colShareRemainder; // ensure all pixels are filled
8732 colWidths[k] = newWidth;
8733 }
8734 }
8735 }
8736 }
8737 }
8738 }
8739 }
8740 }
8741 }
8742
8743 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8744 // TODO: take into account min widths of columns within the span
8745 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
8746 int widthLeft = tableWidthMinusPadding;
8747 int stretchColCount = 0;
8748 for (i = 0; i < m_colCount; i++)
8749 {
8750 // TODO: we need to take into account min widths.
8751 // Subtract min width from width left, then
8752 // add the colShare to the min width
8753 if (colWidths[i] > 0) // absolute or proportional width has been specified
8754 widthLeft -= colWidths[i];
8755 else
8756 {
8757 if (minColWidths[i] > 0)
8758 widthLeft -= minColWidths[i];
8759
8760 stretchColCount ++;
8761 }
8762 }
8763
8764 // Now divide what's left between the remaining columns
8765 int colShare = 0;
8766 if (stretchColCount > 0)
8767 colShare = widthLeft / stretchColCount;
8768 int colShareRemainder = widthLeft - (colShare * stretchColCount);
8769
8770 // Check we don't have enough space, in which case shrink all columns, overriding
8771 // any absolute/proportional widths
8772 // TODO: actually we would like to divide up the shrinkage according to size.
8773 // How do we calculate the proportions that will achieve this?
8774 // Could first choose an arbitrary value for stretching cells, and then calculate
8775 // factors to multiply each width by.
8776 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8777 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
8778 {
8779 colShare = tableWidthMinusPadding / m_colCount;
8780 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
8781 for (i = 0; i < m_colCount; i++)
8782 {
8783 colWidths[i] = 0;
8784 minColWidths[i] = 0;
8785 }
8786 }
8787
8788 // We have to adjust the columns if either we need to shrink the
8789 // table to fit the parent/table width, or we explicitly set the
8790 // table width and need to stretch out the table.
8791 if (widthLeft < 0 || stretchToFitTableWidth)
8792 {
8793 for (i = 0; i < m_colCount; i++)
8794 {
8795 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
8796 {
8797 if (minColWidths[i] > 0)
8798 colWidths[i] = minColWidths[i] + colShare;
8799 else
8800 colWidths[i] = colShare;
8801 if (i == (m_colCount-1))
8802 colWidths[i] += colShareRemainder; // ensure all pixels are filled
8803 }
8804 }
8805 }
8806
8807 // TODO: if spanned cells have no specified or max width, make them the
8808 // as big as the columns they span. Do this for all spanned cells in all
8809 // rows, of course. Size any spanned cells left over at the end - even if they
8810 // have width > 0, make sure they're limited to the appropriate column edge.
8811
8812
8813/*
8814 Sort out confusion between content width
8815 and overall width later. For now, assume we specify overall width.
8816
8817 So, now we've laid out the table to fit into the given space
8818 and have used specified widths and minimum widths.
8819
8820 Now we need to consider how we will try to take maximum width into account.
8821
8822*/
8823
8824 // (??) TODO: take max width into account
8825
8826 // (6) Lay out all cells again with the current values
8827
8828 int maxRight = 0;
8829 int y = availableSpace.y;
8830 for (j = 0; j < m_rowCount; j++)
8831 {
8832 int x = availableSpace.x; // TODO: take into account centering etc.
8833 int maxCellHeight = 0;
8834 int maxSpecifiedCellHeight = 0;
8835
bb7bbd12
JS
8836 wxArrayInt actualWidths;
8837 actualWidths.Add(0, m_colCount);
603f702b
JS
8838
8839 wxTextAttrDimensionConverter converter(dc, scale);
8840 for (i = 0; i < m_colCount; i++)
8841 {
8842 wxRichTextCell* cell = GetCell(j, i);
8843 if (cell->IsShown())
8844 {
603f702b
JS
8845 // Get max specified cell height
8846 // Don't handle percentages for height
8847 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
8848 {
8849 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
8850 if (h > maxSpecifiedCellHeight)
8851 maxSpecifiedCellHeight = h;
8852 }
8853
8854 if (colWidths[i] > 0) // absolute or proportional width has been specified
8855 {
8856 int colSpan = 1;
8857 if (cell->GetProperties().HasProperty(wxT("colspan")))
8858 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8859
8860 wxRect availableCellSpace;
8861
8862 // TODO: take into acount spans
8863 if (colSpan > 1)
8864 {
8865 // Calculate the size of this spanning cell from its constituent columns
8866 int xx = x;
8867 int spans = wxMin(colSpan, m_colCount - i);
8868 for (k = i; k < spans; k++)
8869 {
8870 if (k != i)
8871 xx += paddingX;
8872 xx += colWidths[k];
8873 }
8874 availableCellSpace = wxRect(x, y, xx, -1);
8875 }
8876 else
8877 availableCellSpace = wxRect(x, y, colWidths[i], -1);
8878
8879 // Store actual width so we can force cell to be the appropriate width on the final loop
8880 actualWidths[i] = availableCellSpace.GetWidth();
8881
8882 // Lay out cell
8883 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 8884 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
8885
8886 // TODO: use GetCachedSize().x to compute 'natural' size
8887
8888 x += (availableCellSpace.GetWidth() + paddingX);
8889 if (cell->GetCachedSize().y > maxCellHeight)
8890 maxCellHeight = cell->GetCachedSize().y;
8891 }
8892 }
8893 }
8894
8895 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
8896
8897 for (i = 0; i < m_colCount; i++)
8898 {
8899 wxRichTextCell* cell = GetCell(j, i);
8900 if (cell->IsShown())
8901 {
8902 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
8903 // Lay out cell with new height
8904 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 8905 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
8906
8907 // Make sure the cell size really is the appropriate size,
8908 // not the calculated box size
8909 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
8910
8911 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
8912 }
8913 }
8914
8915 y += maxCellHeight;
8916 if (j < (m_rowCount-1))
8917 y += paddingY;
8918 }
8919
8920 // We need to add back the margins etc.
8921 {
8922 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
8923 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 8924 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
8925 SetCachedSize(marginRect.GetSize());
8926 }
8927
8928 // TODO: calculate max size
8929 {
8930 SetMaxSize(GetCachedSize());
8931 }
8932
8933 // TODO: calculate min size
8934 {
8935 SetMinSize(GetCachedSize());
8936 }
8937
8938 // TODO: currently we use either a fixed table width or the parent's size.
8939 // We also want to be able to calculate the table width from its content,
8940 // whether using fixed column widths or cell content min/max width.
8941 // Probably need a boolean flag to say whether we need to stretch cells
8942 // to fit the table width, or to simply use min/max cell widths. The
8943 // trouble with this is that if cell widths are not specified, they
8944 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8945 // Anyway, ignoring that problem, we probably need to factor layout into a function
8946 // that can can calculate the maximum unconstrained layout in case table size is
8947 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8948 // constrain Layout(), or the previously-calculated max size to constraint layout.
8949
8950 return true;
8951}
8952
8953// Finds the absolute position and row height for the given character position
8db2e3ef 8954bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
8955{
8956 wxRichTextCell* child = GetCell(index+1);
8957 if (child)
8958 {
8959 // Find the position at the start of the child cell, since the table doesn't
8960 // have any caret position of its own.
8db2e3ef 8961 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
8962 }
8963 else
8964 return false;
8965}
8966
8967// Get the cell at the given character position (in the range of the table).
8968wxRichTextCell* wxRichTextTable::GetCell(long pos) const
8969{
8970 int row = 0, col = 0;
8971 if (GetCellRowColumnPosition(pos, row, col))
8972 {
8973 return GetCell(row, col);
8974 }
8975 else
8976 return NULL;
8977}
8978
8979// Get the row/column for a given character position
8980bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
8981{
8982 if (m_colCount == 0 || m_rowCount == 0)
8983 return false;
8984
8985 row = (int) (pos / m_colCount);
8986 col = pos - (row * m_colCount);
8987
8988 wxASSERT(row < m_rowCount && col < m_colCount);
8989
8990 if (row < m_rowCount && col < m_colCount)
8991 return true;
8992 else
8993 return false;
8994}
8995
8996// Calculate range, taking row/cell ordering into account instead of relying
8997// on list ordering.
8998void wxRichTextTable::CalculateRange(long start, long& end)
8999{
9000 long current = start;
9001 long lastEnd = current;
9002
9003 if (IsTopLevel())
9004 {
9005 current = 0;
9006 lastEnd = 0;
9007 }
9008
9009 int i, j;
9010 for (i = 0; i < m_rowCount; i++)
9011 {
9012 for (j = 0; j < m_colCount; j++)
9013 {
9014 wxRichTextCell* child = GetCell(i, j);
9015 if (child)
9016 {
9017 long childEnd = 0;
9018
9019 child->CalculateRange(current, childEnd);
9020
9021 lastEnd = childEnd;
9022 current = childEnd + 1;
9023 }
9024 }
9025 }
9026
9027 // A top-level object always has a range of size 1,
9028 // because its children don't count at this level.
9029 end = start;
9030 m_range.SetRange(start, start);
9031
9032 // An object with no children has zero length
9033 if (m_children.GetCount() == 0)
9034 lastEnd --;
9035 m_ownRange.SetRange(0, lastEnd);
9036}
9037
9038// Gets the range size.
8db2e3ef 9039bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b 9040{
8db2e3ef 9041 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
603f702b
JS
9042}
9043
9044// Deletes content in the given range.
9045bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
9046{
9047 // TODO: implement deletion of cells
9048 return true;
9049}
9050
9051// Gets any text in this object for the given range.
9052wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
9053{
9054 return wxRichTextBox::GetTextForRange(range);
9055}
9056
9057// Copies this object.
9058void wxRichTextTable::Copy(const wxRichTextTable& obj)
9059{
9060 wxRichTextBox::Copy(obj);
9061
9062 ClearTable();
9063
9064 m_rowCount = obj.m_rowCount;
9065 m_colCount = obj.m_colCount;
9066
9067 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
9068
9069 int i, j;
9070 for (i = 0; i < m_rowCount; i++)
9071 {
9072 wxRichTextObjectPtrArray& colArray = m_cells[i];
9073 for (j = 0; j < m_colCount; j++)
9074 {
9075 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
9076 AppendChild(cell);
9077
9078 colArray.Add(cell);
9079 }
9080 }
9081}
9082
9083void wxRichTextTable::ClearTable()
9084{
9085 m_cells.Clear();
9086 DeleteChildren();
9087}
9088
9089bool wxRichTextTable::CreateTable(int rows, int cols)
9090{
9091 ClearTable();
9092
9093 m_rowCount = rows;
9094 m_colCount = cols;
9095
9096 m_cells.Add(wxRichTextObjectPtrArray(), rows);
9097
9098 int i, j;
9099 for (i = 0; i < rows; i++)
9100 {
9101 wxRichTextObjectPtrArray& colArray = m_cells[i];
9102 for (j = 0; j < cols; j++)
9103 {
9104 wxRichTextCell* cell = new wxRichTextCell;
9105 AppendChild(cell);
9106 cell->AddParagraph(wxEmptyString);
9107
9108 colArray.Add(cell);
9109 }
9110 }
9111
9112 return true;
9113}
9114
9115wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
9116{
9117 wxASSERT(row < m_rowCount);
9118 wxASSERT(col < m_colCount);
9119
9120 if (row < m_rowCount && col < m_colCount)
9121 {
9122 wxRichTextObjectPtrArray& colArray = m_cells[row];
9123 wxRichTextObject* obj = colArray[col];
9124 return wxDynamicCast(obj, wxRichTextCell);
9125 }
9126 else
d67faa04 9127 return NULL;
603f702b
JS
9128}
9129
9130// Returns a selection object specifying the selections between start and end character positions.
9131// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9132wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
9133{
9134 wxRichTextSelection selection;
9135 selection.SetContainer((wxRichTextTable*) this);
9136
9137 if (start > end)
9138 {
9139 long tmp = end;
9140 end = start;
9141 start = tmp;
9142 }
9143
9144 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
9145
9146 if (end >= (m_colCount * m_rowCount))
9147 return selection;
9148
9149 // We need to find the rectangle of cells that is described by the rectangle
9150 // with start, end as the diagonal. Make sure we don't add cells that are
9151 // not currenty visible because they are overlapped by spanning cells.
9152/*
9153 --------------------------
9154 | 0 | 1 | 2 | 3 | 4 |
9155 --------------------------
9156 | 5 | 6 | 7 | 8 | 9 |
9157 --------------------------
9158 | 10 | 11 | 12 | 13 | 14 |
9159 --------------------------
9160 | 15 | 16 | 17 | 18 | 19 |
9161 --------------------------
9162
9163 Let's say we select 6 -> 18.
9164
9165 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9166 which is left and which is right.
9167
9168 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9169
9170 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9171 and (b) shown.
9172
9173
9174*/
9175
9176 int leftCol = start - m_colCount * int(start/m_colCount);
9177 int rightCol = end - m_colCount * int(end/m_colCount);
9178
9179 int topRow = int(start/m_colCount);
9180 int bottomRow = int(end/m_colCount);
9181
9182 if (leftCol > rightCol)
9183 {
9184 int tmp = rightCol;
9185 rightCol = leftCol;
9186 leftCol = tmp;
9187 }
9188
9189 if (topRow > bottomRow)
9190 {
9191 int tmp = bottomRow;
9192 bottomRow = topRow;
9193 topRow = tmp;
9194 }
9195
9196 int i, j;
9197 for (i = topRow; i <= bottomRow; i++)
9198 {
9199 for (j = leftCol; j <= rightCol; j++)
9200 {
9201 wxRichTextCell* cell = GetCell(i, j);
9202 if (cell && cell->IsShown())
9203 selection.Add(cell->GetRange());
9204 }
9205 }
9206
9207 return selection;
9208}
9209
9210// Sets the attributes for the cells specified by the selection.
9211bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
9212{
9213 if (selection.GetContainer() != this)
9214 return false;
9215
9216 wxRichTextBuffer* buffer = GetBuffer();
9217 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
9218 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
9219
9220 if (withUndo)
9221 buffer->BeginBatchUndo(_("Set Cell Style"));
9222
9223 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
9224 while (node)
9225 {
9226 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
9227 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
9228 SetStyle(cell, style, flags);
9229 node = node->GetNext();
9230 }
9231
9232 // Do action, or delay it until end of batch.
9233 if (withUndo)
9234 buffer->EndBatchUndo();
9235
9236 return true;
9237}
9238
9239bool wxRichTextTable::DeleteRows(int startRow, int noRows)
9240{
9241 wxASSERT((startRow + noRows) < m_rowCount);
9242 if ((startRow + noRows) >= m_rowCount)
9243 return false;
9244
9245 int i, j;
9246 for (i = startRow; i < (startRow+noRows); i++)
9247 {
9248 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
9249 for (j = 0; j < (int) colArray.GetCount(); j++)
9250 {
9251 wxRichTextObject* cell = colArray[j];
9252 RemoveChild(cell, true);
9253 }
9254
9255 // Keep deleting at the same position, since we move all
9256 // the others up
9257 m_cells.RemoveAt(startRow);
9258 }
9259
9260 m_rowCount = m_rowCount - noRows;
9261
9262 return true;
9263}
9264
9265bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
9266{
9267 wxASSERT((startCol + noCols) < m_colCount);
9268 if ((startCol + noCols) >= m_colCount)
9269 return false;
9270
9271 bool deleteRows = (noCols == m_colCount);
9272
9273 int i, j;
9274 for (i = 0; i < m_rowCount; i++)
9275 {
9276 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
9277 for (j = startCol; j < (startCol+noCols); j++)
9278 {
9279 wxRichTextObject* cell = colArray[j];
9280 RemoveChild(cell, true);
9281 }
9282
9283 if (deleteRows)
9284 m_cells.RemoveAt(0);
9285 }
9286
9287 if (deleteRows)
9288 m_rowCount = 0;
9289 m_colCount = m_colCount - noCols;
9290
9291 return true;
9292}
9293
9294bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
9295{
9296 wxASSERT(startRow <= m_rowCount);
9297 if (startRow > m_rowCount)
9298 return false;
9299
9300 int i, j;
9301 for (i = 0; i < noRows; i++)
9302 {
9303 int idx;
9304 if (startRow == m_rowCount)
9305 {
9306 m_cells.Add(wxRichTextObjectPtrArray());
9307 idx = m_cells.GetCount() - 1;
9308 }
9309 else
9310 {
9311 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
9312 idx = startRow+i;
9313 }
9314
9315 wxRichTextObjectPtrArray& colArray = m_cells[idx];
9316 for (j = 0; j < m_colCount; j++)
9317 {
9318 wxRichTextCell* cell = new wxRichTextCell;
9319 cell->GetAttributes() = attr;
9320
9321 AppendChild(cell);
9322 colArray.Add(cell);
9323 }
9324 }
9325
9326 m_rowCount = m_rowCount + noRows;
9327 return true;
9328}
9329
9330bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
9331{
9332 wxASSERT(startCol <= m_colCount);
9333 if (startCol > m_colCount)
9334 return false;
9335
9336 int i, j;
9337 for (i = 0; i < m_rowCount; i++)
9338 {
9339 wxRichTextObjectPtrArray& colArray = m_cells[i];
9340 for (j = 0; j < noCols; j++)
9341 {
9342 wxRichTextCell* cell = new wxRichTextCell;
9343 cell->GetAttributes() = attr;
9344
9345 AppendChild(cell);
9346
9347 if (startCol == m_colCount)
9348 colArray.Add(cell);
9349 else
9350 colArray.Insert(cell, startCol+j);
9351 }
9352 }
9353
9354 m_colCount = m_colCount + noCols;
9355
9356 return true;
5ad9ae3a
JS
9357}
9358
603f702b
JS
9359// Edit properties via a GUI
9360bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 9361{
603f702b
JS
9362 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
9363 boxDlg.SetAttributes(GetAttributes());
9364
9365 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 9366 {
603f702b
JS
9367 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9368 return true;
5ad9ae3a
JS
9369 }
9370 else
9371 return false;
bec80f4f
JS
9372}
9373
5d7836c4
JS
9374/*
9375 * Module to initialise and clean up handlers
9376 */
9377
9378class wxRichTextModule: public wxModule
9379{
9380DECLARE_DYNAMIC_CLASS(wxRichTextModule)
9381public:
9382 wxRichTextModule() {}
cfa3b256
JS
9383 bool OnInit()
9384 {
d2d0adc7 9385 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
9386 wxRichTextBuffer::InitStandardHandlers();
9387 wxRichTextParagraph::InitDefaultTabs();
9388 return true;
47b378bd 9389 }
cfa3b256
JS
9390 void OnExit()
9391 {
9392 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 9393 wxRichTextBuffer::CleanUpDrawingHandlers();
cfa3b256
JS
9394 wxRichTextDecimalToRoman(-1);
9395 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 9396 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 9397 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 9398 }
5d7836c4
JS
9399};
9400
9401IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
9402
9403
f1d6804f
RD
9404// If the richtext lib is dynamically loaded after the app has already started
9405// (such as from wxPython) then the built-in module system will not init this
9406// module. Provide this function to do it manually.
9407void wxRichTextModuleInit()
9408{
9409 wxModule* module = new wxRichTextModule;
9410 module->Init();
9411 wxModule::RegisterModule(module);
9412}
9413
9414
5d7836c4
JS
9415/*!
9416 * Commands for undo/redo
9417 *
9418 */
9419
9420wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 9421 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 9422{
603f702b 9423 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
9424}
9425
7fe8059f 9426wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
9427{
9428}
9429
9430wxRichTextCommand::~wxRichTextCommand()
9431{
9432 ClearActions();
9433}
9434
9435void wxRichTextCommand::AddAction(wxRichTextAction* action)
9436{
9437 if (!m_actions.Member(action))
9438 m_actions.Append(action);
9439}
9440
9441bool wxRichTextCommand::Do()
9442{
09f14108 9443 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
9444 {
9445 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
9446 action->Do();
9447 }
9448
9449 return true;
9450}
9451
9452bool wxRichTextCommand::Undo()
9453{
09f14108 9454 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
9455 {
9456 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
9457 action->Undo();
9458 }
9459
9460 return true;
9461}
9462
9463void wxRichTextCommand::ClearActions()
9464{
9465 WX_CLEAR_LIST(wxList, m_actions);
9466}
9467
9468/*!
9469 * Individual action
9470 *
9471 */
9472
603f702b
JS
9473wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
9474 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
9475 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
9476{
9477 m_buffer = buffer;
603f702b
JS
9478 m_object = NULL;
9479 m_containerAddress.Create(buffer, container);
5d7836c4
JS
9480 m_ignoreThis = ignoreFirstTime;
9481 m_cmdId = id;
9482 m_position = -1;
9483 m_ctrl = ctrl;
9484 m_name = name;
9485 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
9486 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
9487 if (cmd)
9488 cmd->AddAction(this);
9489}
9490
9491wxRichTextAction::~wxRichTextAction()
9492{
603f702b
JS
9493 if (m_object)
9494 delete m_object;
9495}
9496
9497// Returns the container that this action refers to, using the container address and top-level buffer.
9498wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
9499{
9500 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
9501 return container;
5d7836c4
JS
9502}
9503
603f702b 9504
7051fa41
JS
9505void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
9506{
9507 // Store a list of line start character and y positions so we can figure out which area
9508 // we need to refresh
9509
9510#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
9511 wxRichTextParagraphLayoutBox* container = GetContainer();
9512 wxASSERT(container != NULL);
9513 if (!container)
9514 return;
9515
7051fa41
JS
9516 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9517 // If we had several actions, which only invalidate and leave layout until the
9518 // paint handler is called, then this might not be true. So we may need to switch
9519 // optimisation on only when we're simply adding text and not simultaneously
9520 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9521 // first, but of course this means we'll be doing it twice.
603f702b 9522 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41
JS
9523 {
9524 wxSize clientSize = m_ctrl->GetClientSize();
9525 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
9526 int lastY = firstVisiblePt.y + clientSize.y;
9527
603f702b
JS
9528 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
9529 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
9530 while (node)
9531 {
9532 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
9533 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
9534 while (node2)
9535 {
9536 wxRichTextLine* line = node2->GetData();
9537 wxPoint pt = line->GetAbsolutePosition();
9538 wxRichTextRange range = line->GetAbsoluteRange();
9539
9540 if (pt.y > lastY)
9541 {
9542 node2 = wxRichTextLineList::compatibility_iterator();
9543 node = wxRichTextObjectList::compatibility_iterator();
9544 }
9545 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
9546 {
9547 optimizationLineCharPositions.Add(range.GetStart());
9548 optimizationLineYPositions.Add(pt.y);
9549 }
9550
9551 if (node2)
9552 node2 = node2->GetNext();
9553 }
9554
9555 if (node)
9556 node = node->GetNext();
9557 }
9558 }
9559#endif
9560}
9561
5d7836c4
JS
9562bool wxRichTextAction::Do()
9563{
9564 m_buffer->Modify(true);
9565
603f702b
JS
9566 wxRichTextParagraphLayoutBox* container = GetContainer();
9567 wxASSERT(container != NULL);
9568 if (!container)
9569 return false;
9570
5d7836c4
JS
9571 switch (m_cmdId)
9572 {
9573 case wxRICHTEXT_INSERT:
9574 {
ea160b2e
JS
9575 // Store a list of line start character and y positions so we can figure out which area
9576 // we need to refresh
9577 wxArrayInt optimizationLineCharPositions;
9578 wxArrayInt optimizationLineYPositions;
9579
9580#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 9581 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
9582#endif
9583
603f702b
JS
9584 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
9585 container->UpdateRanges();
9586
9587 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9588 // Layout() would stop prematurely at the top level.
9589 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 9590
603f702b 9591 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
9592
9593 // Character position to caret position
9594 newCaretPosition --;
9595
9596 // Don't take into account the last newline
5d7836c4
JS
9597 if (m_newParagraphs.GetPartialParagraph())
9598 newCaretPosition --;
46ee0e5b 9599 else
7c081bd2 9600 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
9601 {
9602 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
9603 if (p->GetRange().GetLength() == 1)
9604 newCaretPosition --;
9605 }
5d7836c4 9606
603f702b 9607 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 9608
7051fa41 9609 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 9610
5912d19e
JS
9611 wxRichTextEvent cmdEvent(
9612 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
9613 m_ctrl ? m_ctrl->GetId() : -1);
9614 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9615 cmdEvent.SetRange(GetRange());
9616 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9617 cmdEvent.SetContainer(container);
3e541562 9618
5912d19e 9619 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
9620
9621 break;
9622 }
9623 case wxRICHTEXT_DELETE:
9624 {
7051fa41
JS
9625 wxArrayInt optimizationLineCharPositions;
9626 wxArrayInt optimizationLineYPositions;
9627
9628#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9629 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9630#endif
9631
603f702b
JS
9632 container->DeleteRange(GetRange());
9633 container->UpdateRanges();
9634 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9635 // Layout() would stop prematurely at the top level.
9636 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 9637
6ccbca24 9638 long caretPos = GetRange().GetStart()-1;
603f702b 9639 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
9640 caretPos --;
9641
7051fa41 9642 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 9643
5912d19e
JS
9644 wxRichTextEvent cmdEvent(
9645 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
9646 m_ctrl ? m_ctrl->GetId() : -1);
9647 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9648 cmdEvent.SetRange(GetRange());
9649 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9650 cmdEvent.SetContainer(container);
3e541562 9651
5912d19e
JS
9652 m_buffer->SendEvent(cmdEvent);
9653
5d7836c4
JS
9654 break;
9655 }
9656 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 9657 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
9658 {
9659 ApplyParagraphs(GetNewParagraphs());
603f702b
JS
9660
9661 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9662 // Layout() would stop prematurely at the top level.
9663 container->InvalidateHierarchy(GetRange());
9664
9665 UpdateAppearance(GetPosition());
9666
9667 wxRichTextEvent cmdEvent(
590a0f8b 9668 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
9669 m_ctrl ? m_ctrl->GetId() : -1);
9670 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9671 cmdEvent.SetRange(GetRange());
9672 cmdEvent.SetPosition(GetRange().GetStart());
9673 cmdEvent.SetContainer(container);
9674
9675 m_buffer->SendEvent(cmdEvent);
9676
9677 break;
9678 }
9679 case wxRICHTEXT_CHANGE_ATTRIBUTES:
9680 {
9681 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
9682 if (obj)
9683 {
9684 wxRichTextAttr oldAttr = obj->GetAttributes();
9685 obj->GetAttributes() = m_attributes;
9686 m_attributes = oldAttr;
9687 }
9688
9689 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9690 // Layout() would stop prematurely at the top level.
9691 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
9692
9693 UpdateAppearance(GetPosition());
9694
5912d19e
JS
9695 wxRichTextEvent cmdEvent(
9696 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
9697 m_ctrl ? m_ctrl->GetId() : -1);
9698 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9699 cmdEvent.SetRange(GetRange());
9700 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9701 cmdEvent.SetContainer(container);
3e541562 9702
5912d19e
JS
9703 m_buffer->SendEvent(cmdEvent);
9704
603f702b
JS
9705 break;
9706 }
9707 case wxRICHTEXT_CHANGE_OBJECT:
9708 {
9709 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
9710 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9711 if (obj && m_object)
9712 {
9713 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
9714 if (node)
9715 {
9716 wxRichTextObject* obj = node->GetData();
9717 node->SetData(m_object);
9718 m_object = obj;
9719 }
9720 }
9721
9722 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9723 // Layout() would stop prematurely at the top level.
9724 container->InvalidateHierarchy(GetRange());
9725
9726 UpdateAppearance(GetPosition());
9727
9728 // TODO: send new kind of modification event
9729
5d7836c4
JS
9730 break;
9731 }
9732 default:
9733 break;
9734 }
9735
9736 return true;
9737}
9738
9739bool wxRichTextAction::Undo()
9740{
9741 m_buffer->Modify(true);
9742
603f702b
JS
9743 wxRichTextParagraphLayoutBox* container = GetContainer();
9744 wxASSERT(container != NULL);
9745 if (!container)
9746 return false;
9747
5d7836c4
JS
9748 switch (m_cmdId)
9749 {
9750 case wxRICHTEXT_INSERT:
9751 {
7051fa41
JS
9752 wxArrayInt optimizationLineCharPositions;
9753 wxArrayInt optimizationLineYPositions;
9754
9755#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9756 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9757#endif
9758
603f702b
JS
9759 container->DeleteRange(GetRange());
9760 container->UpdateRanges();
9761 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9762 // Layout() would stop prematurely at the top level.
9763 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
9764
9765 long newCaretPosition = GetPosition() - 1;
3e541562 9766
7051fa41 9767 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 9768
5912d19e
JS
9769 wxRichTextEvent cmdEvent(
9770 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
9771 m_ctrl ? m_ctrl->GetId() : -1);
9772 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9773 cmdEvent.SetRange(GetRange());
9774 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9775 cmdEvent.SetContainer(container);
3e541562 9776
5912d19e
JS
9777 m_buffer->SendEvent(cmdEvent);
9778
5d7836c4
JS
9779 break;
9780 }
9781 case wxRICHTEXT_DELETE:
9782 {
7051fa41
JS
9783 wxArrayInt optimizationLineCharPositions;
9784 wxArrayInt optimizationLineYPositions;
9785
9786#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9787 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9788#endif
9789
603f702b
JS
9790 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
9791 container->UpdateRanges();
9792 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9793 // Layout() would stop prematurely at the top level.
9794 container->InvalidateHierarchy(GetRange());
5d7836c4 9795
7051fa41 9796 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 9797
5912d19e
JS
9798 wxRichTextEvent cmdEvent(
9799 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
9800 m_ctrl ? m_ctrl->GetId() : -1);
9801 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9802 cmdEvent.SetRange(GetRange());
9803 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9804 cmdEvent.SetContainer(container);
3e541562 9805
5912d19e
JS
9806 m_buffer->SendEvent(cmdEvent);
9807
5d7836c4
JS
9808 break;
9809 }
9810 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 9811 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
9812 {
9813 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
9814 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9815 // Layout() would stop prematurely at the top level.
9816 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
9817
9818 UpdateAppearance(GetPosition());
9819
5912d19e 9820 wxRichTextEvent cmdEvent(
590a0f8b 9821 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
9822 m_ctrl ? m_ctrl->GetId() : -1);
9823 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9824 cmdEvent.SetRange(GetRange());
9825 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9826 cmdEvent.SetContainer(container);
3e541562 9827
5912d19e
JS
9828 m_buffer->SendEvent(cmdEvent);
9829
5d7836c4
JS
9830 break;
9831 }
603f702b
JS
9832 case wxRICHTEXT_CHANGE_ATTRIBUTES:
9833 case wxRICHTEXT_CHANGE_OBJECT:
9834 {
9835 return Do();
9836 }
5d7836c4
JS
9837 default:
9838 break;
9839 }
9840
9841 return true;
9842}
9843
9844/// Update the control appearance
603f702b 9845void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 9846{
603f702b
JS
9847 wxRichTextParagraphLayoutBox* container = GetContainer();
9848 wxASSERT(container != NULL);
9849 if (!container)
9850 return;
9851
5d7836c4
JS
9852 if (m_ctrl)
9853 {
603f702b 9854 m_ctrl->SetFocusObject(container);
5d7836c4 9855 m_ctrl->SetCaretPosition(caretPosition);
603f702b 9856
5d7836c4
JS
9857 if (!m_ctrl->IsFrozen())
9858 {
603f702b
JS
9859 wxRect containerRect = container->GetRect();
9860
2f36e8dc 9861 m_ctrl->LayoutContent();
5d7836c4 9862
603f702b
JS
9863 // Refresh everything if there were floating objects or the container changed size
9864 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9865 if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
9866 {
9867 m_ctrl->Refresh(false);
9868 }
9869 else
9870
9871#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9872 // Find refresh rectangle if we are in a position to optimise refresh
9873 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
9874 {
9875 size_t i;
9876
9877 wxSize clientSize = m_ctrl->GetClientSize();
9878 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
9879
9880 // Start/end positions
9881 int firstY = 0;
9882 int lastY = firstVisiblePt.y + clientSize.y;
9883
9884 bool foundEnd = false;
9885
9886 // position offset - how many characters were inserted
9887 int positionOffset = GetRange().GetLength();
9888
9889 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9890 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
9891 positionOffset = - positionOffset;
9892
9893 // find the first line which is being drawn at the same position as it was
9894 // before. Since we're talking about a simple insertion, we can assume
9895 // that the rest of the window does not need to be redrawn.
9896
9897 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
9898 // Since we support floating layout, we should redraw the whole para instead of just
9899 // the first line touching the invalid range.
9900 if (para)
9901 {
9902 firstY = para->GetPosition().y;
9903 }
9904
9905 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
9906 while (node)
9907 {
9908 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
9909 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
9910 while (node2)
9911 {
9912 wxRichTextLine* line = node2->GetData();
9913 wxPoint pt = line->GetAbsolutePosition();
9914 wxRichTextRange range = line->GetAbsoluteRange();
9915
9916 // we want to find the first line that is in the same position
9917 // as before. This will mean we're at the end of the changed text.
9918
9919 if (pt.y > lastY) // going past the end of the window, no more info
9920 {
9921 node2 = wxRichTextLineList::compatibility_iterator();
9922 node = wxRichTextObjectList::compatibility_iterator();
9923 }
9924 // Detect last line in the buffer
9925 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
9926 {
9927 // If deleting text, make sure we refresh below as well as above
9928 if (positionOffset >= 0)
9929 {
9930 foundEnd = true;
9931 lastY = pt.y + line->GetSize().y;
9932 }
9933
9934 node2 = wxRichTextLineList::compatibility_iterator();
9935 node = wxRichTextObjectList::compatibility_iterator();
9936
9937 break;
9938 }
9939 else
9940 {
9941 // search for this line being at the same position as before
9942 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
9943 {
9944 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
9945 ((*optimizationLineYPositions)[i] == pt.y))
9946 {
9947 // Stop, we're now the same as we were
9948 foundEnd = true;
9949
9950 lastY = pt.y;
9951
9952 node2 = wxRichTextLineList::compatibility_iterator();
9953 node = wxRichTextObjectList::compatibility_iterator();
9954
9955 break;
9956 }
9957 }
9958 }
9959
9960 if (node2)
9961 node2 = node2->GetNext();
9962 }
9963
9964 if (node)
9965 node = node->GetNext();
9966 }
9967
9968 firstY = wxMax(firstVisiblePt.y, firstY);
9969 if (!foundEnd)
9970 lastY = firstVisiblePt.y + clientSize.y;
9971
9972 // Convert to device coordinates
9973 wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY));
9974 m_ctrl->RefreshRect(rect);
9975 }
9976 else
1c13f06e 9977#endif
603f702b
JS
9978 m_ctrl->Refresh(false);
9979
9980 m_ctrl->PositionCaret();
4fe83b93
JS
9981
9982 // This causes styles to persist when doing programmatic
9983 // content creation except when Freeze/Thaw is used, so
9984 // disable this and check for the consequences.
9985 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 9986
5d7836c4 9987 if (sendUpdateEvent)
0ec1179b 9988 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 9989 }
7fe8059f 9990 }
5d7836c4
JS
9991}
9992
9993/// Replace the buffer paragraphs with the new ones.
0ca07313 9994void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 9995{
603f702b
JS
9996 wxRichTextParagraphLayoutBox* container = GetContainer();
9997 wxASSERT(container != NULL);
9998 if (!container)
9999 return;
10000
5d7836c4
JS
10001 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
10002 while (node)
10003 {
10004 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
10005 wxASSERT (para != NULL);
10006
10007 // We'll replace the existing paragraph by finding the paragraph at this position,
10008 // delete its node data, and setting a copy as the new node data.
10009 // TODO: make more efficient by simply swapping old and new paragraph objects.
10010
603f702b 10011 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
10012 if (existingPara)
10013 {
603f702b 10014 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
10015 if (bufferParaNode)
10016 {
10017 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 10018 newPara->SetParent(container);
5d7836c4
JS
10019
10020 bufferParaNode->SetData(newPara);
10021
10022 delete existingPara;
10023 }
10024 }
10025
10026 node = node->GetNext();
10027 }
10028}
10029
10030
10031/*!
10032 * wxRichTextRange
10033 * This stores beginning and end positions for a range of data.
10034 */
10035
603f702b
JS
10036WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
10037
5d7836c4
JS
10038/// Limit this range to be within 'range'
10039bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
10040{
10041 if (m_start < range.m_start)
10042 m_start = range.m_start;
10043
10044 if (m_end > range.m_end)
10045 m_end = range.m_end;
10046
10047 return true;
10048}
10049
10050/*!
10051 * wxRichTextImage implementation
10052 * This object represents an image.
10053 */
10054
bec80f4f 10055IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 10056
24777478 10057wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 10058 wxRichTextObject(parent)
5d7836c4 10059{
cdaed652 10060 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
10061 if (charStyle)
10062 SetAttributes(*charStyle);
5d7836c4
JS
10063}
10064
24777478 10065wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 10066 wxRichTextObject(parent)
5d7836c4
JS
10067{
10068 m_imageBlock = imageBlock;
4f32b3cf
JS
10069 if (charStyle)
10070 SetAttributes(*charStyle);
5d7836c4
JS
10071}
10072
cdaed652
VZ
10073/// Create a cached image at the required size
10074bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
5d7836c4 10075{
cdaed652
VZ
10076 if (resetCache || !m_imageCache.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
10077 {
10078 if (!m_imageBlock.IsOk())
10079 return false;
ce00f59b 10080
cdaed652
VZ
10081 wxImage image;
10082 m_imageBlock.Load(image);
10083 if (!image.IsOk())
10084 return false;
ce00f59b 10085
cdaed652
VZ
10086 int width = image.GetWidth();
10087 int height = image.GetHeight();
bec80f4f 10088
603f702b 10089 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
cdaed652 10090 {
24777478
JS
10091 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10092 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
ce00f59b 10093 else
24777478 10094 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
cdaed652 10095 }
603f702b 10096 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
cdaed652 10097 {
24777478
JS
10098 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10099 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
cdaed652 10100 else
24777478 10101 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
cdaed652 10102 }
5d7836c4 10103
cdaed652
VZ
10104 if (image.GetWidth() == width && image.GetHeight() == height)
10105 m_imageCache = wxBitmap(image);
10106 else
10107 {
10108 // If the original width and height is small, e.g. 400 or below,
10109 // scale up and then down to improve image quality. This can make
10110 // a big difference, with not much performance hit.
10111 int upscaleThreshold = 400;
10112 wxImage img;
10113 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
10114 {
10115 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
10116 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
10117 }
10118 else
10119 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
10120 m_imageCache = wxBitmap(img);
10121 }
10122 }
ce00f59b 10123
cdaed652 10124 return m_imageCache.IsOk();
5d7836c4
JS
10125}
10126
5d7836c4 10127/// Draw the item
8db2e3ef 10128bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 10129{
603f702b
JS
10130 if (!IsShown())
10131 return true;
10132
cdaed652
VZ
10133 // Don't need cached size AFAIK
10134 // wxSize size = GetCachedSize();
10135 if (!LoadImageCache(dc))
5d7836c4 10136 return false;
ce00f59b 10137
8db2e3ef
JS
10138 wxRichTextAttr attr(GetAttributes());
10139 context.ApplyVirtualAttributes(attr, this);
10140
10141 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b
JS
10142
10143#if 0
cdaed652 10144 int y = rect.y + (rect.height - m_imageCache.GetHeight());
5d7836c4 10145
cdaed652 10146 dc.DrawBitmap(m_imageCache, rect.x, y, true);
603f702b
JS
10147#endif
10148
10149 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10150 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10151 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 10152 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10153
10154 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 10155
603f702b 10156 if (selection.WithinSelection(range.GetStart(), this))
5d7836c4 10157 {
ecb5fbf1
JS
10158 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
10159 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 10160 dc.SetLogicalFunction(wxINVERT);
603f702b 10161 dc.DrawRectangle(contentRect);
5d7836c4
JS
10162 dc.SetLogicalFunction(wxCOPY);
10163 }
10164
10165 return true;
10166}
10167
10168/// Lay the item out
8db2e3ef 10169bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 10170{
cdaed652
VZ
10171 if (!LoadImageCache(dc))
10172 return false;
5d7836c4 10173
603f702b
JS
10174 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10175 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10176 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
10177
10178 wxRichTextAttr attr(GetAttributes());
10179 context.ApplyVirtualAttributes(attr, this);
10180
10181 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10182
10183 wxSize overallSize = marginRect.GetSize();
10184
10185 SetCachedSize(overallSize);
10186 SetMaxSize(overallSize);
10187 SetMinSize(overallSize);
cdaed652 10188 SetPosition(rect.GetPosition());
5d7836c4
JS
10189
10190 return true;
10191}
10192
10193/// Get/set the object size for the given range. Returns false if the range
10194/// is invalid for this object.
8db2e3ef 10195bool 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
10196{
10197 if (!range.IsWithin(GetRange()))
10198 return false;
10199
cdaed652 10200 if (!((wxRichTextImage*)this)->LoadImageCache(dc))
31778480 10201 {
cdaed652
VZ
10202 size.x = 0; size.y = 0;
10203 if (partialExtents)
31778480 10204 partialExtents->Add(0);
cdaed652 10205 return false;
31778480 10206 }
ce00f59b 10207
8db2e3ef
JS
10208 wxRichTextAttr attr(GetAttributes());
10209 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
10210
603f702b
JS
10211 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10212 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10213 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef 10214 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10215
10216 wxSize overallSize = marginRect.GetSize();
31778480 10217
cdaed652 10218 if (partialExtents)
603f702b 10219 partialExtents->Add(overallSize.x);
5d7836c4 10220
603f702b 10221 size = overallSize;
5d7836c4
JS
10222
10223 return true;
10224}
10225
603f702b
JS
10226// Get the 'natural' size for an object. For an image, it would be the
10227// image size.
10228wxTextAttrSize wxRichTextImage::GetNaturalSize() const
10229{
10230 wxTextAttrSize size;
10231 if (GetImageCache().IsOk())
10232 {
10233 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
10234 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
10235 }
10236 return size;
10237}
10238
10239
5d7836c4
JS
10240/// Copy
10241void wxRichTextImage::Copy(const wxRichTextImage& obj)
10242{
bec80f4f 10243 wxRichTextObject::Copy(obj);
59509217 10244
5d7836c4
JS
10245 m_imageBlock = obj.m_imageBlock;
10246}
10247
cdaed652
VZ
10248/// Edit properties via a GUI
10249bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10250{
603f702b
JS
10251 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
10252 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
10253
10254 if (imageDlg.ShowModal() == wxID_OK)
10255 {
603f702b
JS
10256 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10257 // indeterminate in the object.
10258 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
10259 return true;
10260 }
10261 else
10262 return false;
10263}
10264
5d7836c4
JS
10265/*!
10266 * Utilities
10267 *
10268 */
10269
10270/// Compare two attribute objects
24777478 10271bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 10272{
38f833b1 10273 return (attr1 == attr2);
5d7836c4
JS
10274}
10275
44cc96a8 10276// Partial equality test taking flags into account
24777478 10277bool wxTextAttrEqPartial(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
44cc96a8 10278{
24777478 10279 return attr1.EqPartial(attr2);
44cc96a8 10280}
5d7836c4 10281
44cc96a8
JS
10282/// Compare tabs
10283bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
10284{
10285 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
10286 return false;
10287
44cc96a8
JS
10288 size_t i;
10289 for (i = 0; i < tabs1.GetCount(); i++)
10290 {
10291 if (tabs1[i] != tabs2[i])
10292 return false;
10293 }
10294 return true;
10295}
5d7836c4 10296
24777478 10297bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
10298{
10299 return destStyle.Apply(style, compareWith);
10300}
5d7836c4 10301
44cc96a8 10302// Remove attributes
24777478 10303bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 10304{
24777478 10305 return destStyle.RemoveStyle(style);
44cc96a8 10306}
5d7836c4 10307
44cc96a8
JS
10308/// Combine two bitlists, specifying the bits of interest with separate flags.
10309bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
10310{
24777478 10311 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 10312}
5d7836c4 10313
44cc96a8
JS
10314/// Compare two bitlists
10315bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
10316{
24777478 10317 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 10318}
38f833b1 10319
44cc96a8 10320/// Split into paragraph and character styles
24777478 10321bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 10322{
24777478 10323 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 10324}
5d7836c4 10325
44cc96a8
JS
10326/// Convert a decimal to Roman numerals
10327wxString wxRichTextDecimalToRoman(long n)
10328{
10329 static wxArrayInt decimalNumbers;
10330 static wxArrayString romanNumbers;
5d7836c4 10331
44cc96a8
JS
10332 // Clean up arrays
10333 if (n == -1)
10334 {
10335 decimalNumbers.Clear();
10336 romanNumbers.Clear();
10337 return wxEmptyString;
10338 }
5d7836c4 10339
44cc96a8
JS
10340 if (decimalNumbers.GetCount() == 0)
10341 {
10342 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 10343
44cc96a8
JS
10344 wxRichTextAddDecRom(1000, wxT("M"));
10345 wxRichTextAddDecRom(900, wxT("CM"));
10346 wxRichTextAddDecRom(500, wxT("D"));
10347 wxRichTextAddDecRom(400, wxT("CD"));
10348 wxRichTextAddDecRom(100, wxT("C"));
10349 wxRichTextAddDecRom(90, wxT("XC"));
10350 wxRichTextAddDecRom(50, wxT("L"));
10351 wxRichTextAddDecRom(40, wxT("XL"));
10352 wxRichTextAddDecRom(10, wxT("X"));
10353 wxRichTextAddDecRom(9, wxT("IX"));
10354 wxRichTextAddDecRom(5, wxT("V"));
10355 wxRichTextAddDecRom(4, wxT("IV"));
10356 wxRichTextAddDecRom(1, wxT("I"));
10357 }
5d7836c4 10358
44cc96a8
JS
10359 int i = 0;
10360 wxString roman;
ea160b2e 10361
44cc96a8 10362 while (n > 0 && i < 13)
42688aea 10363 {
44cc96a8
JS
10364 if (n >= decimalNumbers[i])
10365 {
10366 n -= decimalNumbers[i];
10367 roman += romanNumbers[i];
10368 }
10369 else
10370 {
10371 i ++;
10372 }
42688aea 10373 }
44cc96a8
JS
10374 if (roman.IsEmpty())
10375 roman = wxT("0");
10376 return roman;
10377}
42688aea 10378
44cc96a8
JS
10379/*!
10380 * wxRichTextFileHandler
10381 * Base class for file handlers
10382 */
4d6d8bf4 10383
44cc96a8 10384IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 10385
44cc96a8
JS
10386#if wxUSE_FFILE && wxUSE_STREAMS
10387bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 10388{
44cc96a8 10389 wxFFileInputStream stream(filename);
a1b806b9 10390 if (stream.IsOk())
44cc96a8 10391 return LoadFile(buffer, stream);
5d7836c4 10392
44cc96a8
JS
10393 return false;
10394}
5d7836c4 10395
44cc96a8
JS
10396bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
10397{
10398 wxFFileOutputStream stream(filename);
a1b806b9 10399 if (stream.IsOk())
44cc96a8 10400 return SaveFile(buffer, stream);
5d7836c4 10401
44cc96a8
JS
10402 return false;
10403}
10404#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 10405
44cc96a8
JS
10406/// Can we handle this filename (if using files)? By default, checks the extension.
10407bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
10408{
10409 wxString path, file, ext;
a51e601e 10410 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 10411
44cc96a8
JS
10412 return (ext.Lower() == GetExtension());
10413}
5d7836c4 10414
44cc96a8
JS
10415/*!
10416 * wxRichTextTextHandler
10417 * Plain text handler
10418 */
5d7836c4 10419
44cc96a8 10420IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 10421
44cc96a8
JS
10422#if wxUSE_STREAMS
10423bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
10424{
10425 if (!stream.IsOk())
797e38dd
JS
10426 return false;
10427
44cc96a8
JS
10428 wxString str;
10429 int lastCh = 0;
5d7836c4 10430
44cc96a8
JS
10431 while (!stream.Eof())
10432 {
10433 int ch = stream.GetC();
5d7836c4 10434
44cc96a8
JS
10435 if (!stream.Eof())
10436 {
10437 if (ch == 10 && lastCh != 13)
10438 str += wxT('\n');
5d7836c4 10439
44cc96a8
JS
10440 if (ch > 0 && ch != 10)
10441 str += wxChar(ch);
5d7836c4 10442
44cc96a8
JS
10443 lastCh = ch;
10444 }
10445 }
5d7836c4 10446
44cc96a8
JS
10447 buffer->ResetAndClearCommands();
10448 buffer->Clear();
10449 buffer->AddParagraphs(str);
10450 buffer->UpdateRanges();
5d7836c4 10451
44cc96a8
JS
10452 return true;
10453}
5d7836c4 10454
44cc96a8
JS
10455bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
10456{
10457 if (!stream.IsOk())
5d7836c4
JS
10458 return false;
10459
44cc96a8 10460 wxString text = buffer->GetText();
38f833b1 10461
44cc96a8
JS
10462 wxString newLine = wxRichTextLineBreakChar;
10463 text.Replace(newLine, wxT("\n"));
5d7836c4 10464
44cc96a8 10465 wxCharBuffer buf = text.ToAscii();
5d7836c4 10466
44cc96a8
JS
10467 stream.Write((const char*) buf, text.length());
10468 return true;
10469}
10470#endif // wxUSE_STREAMS
5d7836c4 10471
44cc96a8
JS
10472/*
10473 * Stores information about an image, in binary in-memory form
10474 */
59509217 10475
44cc96a8
JS
10476wxRichTextImageBlock::wxRichTextImageBlock()
10477{
10478 Init();
10479}
5d7836c4 10480
44cc96a8
JS
10481wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
10482{
10483 Init();
10484 Copy(block);
10485}
ea160b2e 10486
44cc96a8
JS
10487wxRichTextImageBlock::~wxRichTextImageBlock()
10488{
5276b0a5 10489 wxDELETEA(m_data);
5d7836c4
JS
10490}
10491
44cc96a8 10492void wxRichTextImageBlock::Init()
5d7836c4
JS
10493{
10494 m_data = NULL;
10495 m_dataSize = 0;
d75a69e8 10496 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
10497}
10498
10499void wxRichTextImageBlock::Clear()
10500{
5276b0a5 10501 wxDELETEA(m_data);
5d7836c4 10502 m_dataSize = 0;
d75a69e8 10503 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
10504}
10505
10506
10507// Load the original image into a memory block.
10508// If the image is not a JPEG, we must convert it into a JPEG
10509// to conserve space.
10510// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10511// load the image a 2nd time.
10512
d75a69e8
FM
10513bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
10514 wxImage& image, bool convertToJPEG)
5d7836c4
JS
10515{
10516 m_imageType = imageType;
10517
10518 wxString filenameToRead(filename);
7fe8059f 10519 bool removeFile = false;
5d7836c4 10520
62891c87 10521 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 10522 return false; // Could not determine image type
5d7836c4
JS
10523
10524 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
10525 {
a51e601e
FM
10526 wxString tempFile =
10527 wxFileName::CreateTempFileName(_("image"));
5d7836c4 10528
a51e601e 10529 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
10530
10531 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
10532 filenameToRead = tempFile;
7fe8059f 10533 removeFile = true;
5d7836c4
JS
10534
10535 m_imageType = wxBITMAP_TYPE_JPEG;
10536 }
10537 wxFile file;
10538 if (!file.Open(filenameToRead))
7fe8059f 10539 return false;
5d7836c4
JS
10540
10541 m_dataSize = (size_t) file.Length();
10542 file.Close();
10543
10544 if (m_data)
10545 delete[] m_data;
10546 m_data = ReadBlock(filenameToRead, m_dataSize);
10547
10548 if (removeFile)
10549 wxRemoveFile(filenameToRead);
10550
10551 return (m_data != NULL);
10552}
10553
10554// Make an image block from the wxImage in the given
10555// format.
d75a69e8 10556bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 10557{
5d7836c4
JS
10558 image.SetOption(wxT("quality"), quality);
10559
62891c87 10560 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 10561 return false; // Could not determine image type
5d7836c4 10562
cdaed652
VZ
10563 return DoMakeImageBlock(image, imageType);
10564}
10565
10566// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10567bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
10568{
10569 if (imageType == wxBITMAP_TYPE_INVALID)
10570 return false; // Could not determine image type
ce00f59b 10571
cdaed652
VZ
10572 return DoMakeImageBlock(image, imageType);
10573}
7fe8059f 10574
cdaed652
VZ
10575// Makes the image block
10576bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
10577{
10578 wxMemoryOutputStream memStream;
10579 if (!image.SaveFile(memStream, imageType))
5d7836c4 10580 {
7fe8059f 10581 return false;
5d7836c4 10582 }
ce00f59b 10583
cdaed652
VZ
10584 unsigned char* block = new unsigned char[memStream.GetSize()];
10585 if (!block)
377c1ba4 10586 return false;
ce00f59b 10587
5d7836c4
JS
10588 if (m_data)
10589 delete[] m_data;
cdaed652 10590 m_data = block;
ce00f59b
VZ
10591
10592 m_imageType = imageType;
cdaed652 10593 m_dataSize = memStream.GetSize();
5d7836c4 10594
cdaed652 10595 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
10596
10597 return (m_data != NULL);
10598}
10599
5d7836c4
JS
10600// Write to a file
10601bool wxRichTextImageBlock::Write(const wxString& filename)
10602{
10603 return WriteBlock(filename, m_data, m_dataSize);
10604}
10605
10606void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
10607{
10608 m_imageType = block.m_imageType;
5276b0a5 10609 wxDELETEA(m_data);
5d7836c4
JS
10610 m_dataSize = block.m_dataSize;
10611 if (m_dataSize == 0)
10612 return;
10613
10614 m_data = new unsigned char[m_dataSize];
10615 unsigned int i;
10616 for (i = 0; i < m_dataSize; i++)
10617 m_data[i] = block.m_data[i];
10618}
10619
10620//// Operators
10621void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
10622{
10623 Copy(block);
10624}
10625
10626// Load a wxImage from the block
10627bool wxRichTextImageBlock::Load(wxImage& image)
10628{
10629 if (!m_data)
7fe8059f 10630 return false;
5d7836c4
JS
10631
10632 // Read in the image.
0ca07313 10633#if wxUSE_STREAMS
5d7836c4
JS
10634 wxMemoryInputStream mstream(m_data, m_dataSize);
10635 bool success = image.LoadFile(mstream, GetImageType());
10636#else
a51e601e
FM
10637 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
10638 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
10639
10640 if (!WriteBlock(tempFile, m_data, m_dataSize))
10641 {
7fe8059f 10642 return false;
5d7836c4
JS
10643 }
10644 success = image.LoadFile(tempFile, GetImageType());
10645 wxRemoveFile(tempFile);
10646#endif
10647
10648 return success;
10649}
10650
10651// Write data in hex to a stream
10652bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
10653{
4dc7ae1a
JS
10654 if (m_dataSize == 0)
10655 return true;
10656
10657 int bufSize = 100000;
a3c12576
JS
10658 if (int(2*m_dataSize) < bufSize)
10659 bufSize = 2*m_dataSize;
4dc7ae1a 10660 char* buf = new char[bufSize+1];
351c0647
JS
10661
10662 int left = m_dataSize;
10663 int n, i, j;
10664 j = 0;
10665 while (left > 0)
5d7836c4 10666 {
351c0647
JS
10667 if (left*2 > bufSize)
10668 {
10669 n = bufSize; left -= (bufSize/2);
10670 }
10671 else
10672 {
10673 n = left*2; left = 0;
10674 }
7fe8059f 10675
351c0647
JS
10676 char* b = buf;
10677 for (i = 0; i < (n/2); i++)
10678 {
f728025e 10679 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
10680 b += 2; j ++;
10681 }
5d7836c4 10682
351c0647
JS
10683 buf[n] = 0;
10684 stream.Write((const char*) buf, n);
10685 }
4dc7ae1a 10686 delete[] buf;
5d7836c4
JS
10687 return true;
10688}
10689
10690// Read data in hex from a stream
d75a69e8 10691bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
10692{
10693 int dataSize = length/2;
10694
10695 if (m_data)
10696 delete[] m_data;
10697
046fce47
FM
10698 // create a null terminated temporary string:
10699 char str[3];
10700 str[2] = '\0';
10701
5d7836c4
JS
10702 m_data = new unsigned char[dataSize];
10703 int i;
10704 for (i = 0; i < dataSize; i ++)
10705 {
c9f78968
VS
10706 str[0] = (char)stream.GetC();
10707 str[1] = (char)stream.GetC();
5d7836c4 10708
a9465653 10709 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
10710 }
10711
10712 m_dataSize = dataSize;
10713 m_imageType = imageType;
10714
10715 return true;
10716}
10717
5d7836c4
JS
10718// Allocate and read from stream as a block of memory
10719unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
10720{
10721 unsigned char* block = new unsigned char[size];
10722 if (!block)
10723 return NULL;
10724
10725 stream.Read(block, size);
10726
10727 return block;
10728}
10729
10730unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
10731{
10732 wxFileInputStream stream(filename);
a1b806b9 10733 if (!stream.IsOk())
5d7836c4
JS
10734 return NULL;
10735
10736 return ReadBlock(stream, size);
10737}
10738
10739// Write memory block to stream
10740bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
10741{
10742 stream.Write((void*) block, size);
10743 return stream.IsOk();
10744
10745}
10746
10747// Write memory block to file
10748bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
10749{
10750 wxFileOutputStream outStream(filename);
a1b806b9 10751 if (!outStream.IsOk())
7fe8059f 10752 return false;
5d7836c4
JS
10753
10754 return WriteBlock(outStream, block, size);
10755}
10756
d2d0adc7
JS
10757// Gets the extension for the block's type
10758wxString wxRichTextImageBlock::GetExtension() const
10759{
10760 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
10761 if (handler)
10762 return handler->GetExtension();
10763 else
10764 return wxEmptyString;
10765}
10766
0ca07313
JS
10767#if wxUSE_DATAOBJ
10768
10769/*!
10770 * The data object for a wxRichTextBuffer
10771 */
10772
10773const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
10774
10775wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
10776{
10777 m_richTextBuffer = richTextBuffer;
10778
10779 // this string should uniquely identify our format, but is otherwise
10780 // arbitrary
10781 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
10782
10783 SetFormat(m_formatRichTextBuffer);
10784}
10785
10786wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10787{
10788 delete m_richTextBuffer;
10789}
10790
10791// after a call to this function, the richTextBuffer is owned by the caller and it
10792// is responsible for deleting it!
10793wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
10794{
10795 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
10796 m_richTextBuffer = NULL;
10797
10798 return richTextBuffer;
10799}
10800
10801wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
10802{
10803 return m_formatRichTextBuffer;
10804}
10805
10806size_t wxRichTextBufferDataObject::GetDataSize() const
10807{
10808 if (!m_richTextBuffer)
10809 return 0;
10810
10811 wxString bufXML;
10812
10813 {
10814 wxStringOutputStream stream(& bufXML);
10815 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
10816 {
10817 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10818 return 0;
10819 }
10820 }
10821
10822#if wxUSE_UNICODE
10823 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
10824 return strlen(buffer) + 1;
10825#else
10826 return bufXML.Length()+1;
10827#endif
10828}
10829
10830bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
10831{
10832 if (!pBuf || !m_richTextBuffer)
10833 return false;
10834
10835 wxString bufXML;
10836
10837 {
10838 wxStringOutputStream stream(& bufXML);
10839 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
10840 {
10841 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10842 return 0;
10843 }
10844 }
10845
10846#if wxUSE_UNICODE
10847 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
10848 size_t len = strlen(buffer);
10849 memcpy((char*) pBuf, (const char*) buffer, len);
10850 ((char*) pBuf)[len] = 0;
10851#else
10852 size_t len = bufXML.Length();
10853 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
10854 ((char*) pBuf)[len] = 0;
10855#endif
10856
10857 return true;
10858}
10859
10860bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
10861{
5276b0a5 10862 wxDELETE(m_richTextBuffer);
0ca07313
JS
10863
10864 wxString bufXML((const char*) buf, wxConvUTF8);
10865
10866 m_richTextBuffer = new wxRichTextBuffer;
10867
10868 wxStringInputStream stream(bufXML);
10869 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
10870 {
10871 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10872
5276b0a5 10873 wxDELETE(m_richTextBuffer);
0ca07313
JS
10874
10875 return false;
10876 }
10877 return true;
10878}
10879
10880#endif
10881 // wxUSE_DATAOBJ
10882
44cc96a8
JS
10883
10884/*
10885 * wxRichTextFontTable
10886 * Manages quick access to a pool of fonts for rendering rich text
10887 */
10888
d65381ac 10889WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
10890
10891class wxRichTextFontTableData: public wxObjectRefData
10892{
10893public:
10894 wxRichTextFontTableData() {}
10895
24777478 10896 wxFont FindFont(const wxRichTextAttr& fontSpec);
44cc96a8
JS
10897
10898 wxRichTextFontTableHashMap m_hashMap;
10899};
10900
24777478 10901wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
10902{
10903 wxString facename(fontSpec.GetFontFaceName());
10904 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()));
10905 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
10906
10907 if ( entry == m_hashMap.end() )
10908 {
10909 wxFont font(fontSpec.GetFontSize(), wxDEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
10910 m_hashMap[spec] = font;
10911 return font;
10912 }
10913 else
10914 {
10915 return entry->second;
10916 }
10917}
10918
10919IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
10920
10921wxRichTextFontTable::wxRichTextFontTable()
10922{
10923 m_refData = new wxRichTextFontTableData;
44cc96a8
JS
10924}
10925
10926wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 10927 : wxObject()
44cc96a8
JS
10928{
10929 (*this) = table;
10930}
10931
10932wxRichTextFontTable::~wxRichTextFontTable()
10933{
10934 UnRef();
10935}
10936
10937bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
10938{
10939 return (m_refData == table.m_refData);
10940}
10941
10942void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
10943{
10944 Ref(table);
10945}
10946
24777478 10947wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
10948{
10949 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
10950 if (data)
10951 return data->FindFont(fontSpec);
10952 else
10953 return wxFont();
10954}
10955
10956void wxRichTextFontTable::Clear()
10957{
10958 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
10959 if (data)
10960 data->m_hashMap.clear();
10961}
10962
24777478
JS
10963// wxTextBoxAttr
10964
10965
10966void wxTextBoxAttr::Reset()
10967{
10968 m_flags = 0;
603f702b
JS
10969 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
10970 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
10971 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
10972 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 10973 m_boxStyleName = wxEmptyString;
bec80f4f 10974
24777478
JS
10975 m_margins.Reset();
10976 m_padding.Reset();
10977 m_position.Reset();
10978
603f702b 10979 m_size.Reset();
303f0be7
JS
10980 m_minSize.Reset();
10981 m_maxSize.Reset();
24777478
JS
10982
10983 m_border.Reset();
10984 m_outline.Reset();
10985}
10986
10987// Equality test
10988bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
10989{
10990 return (
10991 m_flags == attr.m_flags &&
10992 m_floatMode == attr.m_floatMode &&
10993 m_clearMode == attr.m_clearMode &&
10994 m_collapseMode == attr.m_collapseMode &&
603f702b 10995 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 10996
24777478
JS
10997 m_margins == attr.m_margins &&
10998 m_padding == attr.m_padding &&
10999 m_position == attr.m_position &&
11000
603f702b 11001 m_size == attr.m_size &&
303f0be7
JS
11002 m_minSize == attr.m_minSize &&
11003 m_maxSize == attr.m_maxSize &&
24777478
JS
11004
11005 m_border == attr.m_border &&
2f987d83
JS
11006 m_outline == attr.m_outline &&
11007
11008 m_boxStyleName == attr.m_boxStyleName
24777478
JS
11009 );
11010}
11011
11012// Partial equality test
11013bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr) const
11014{
11015 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
11016 return false;
11017
11018 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
11019 return false;
11020
11021 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
11022 return false;
11023
603f702b
JS
11024 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
11025 return false;
11026
2f987d83
JS
11027 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
11028 return false;
11029
24777478
JS
11030 // Position
11031
11032 if (!m_position.EqPartial(attr.m_position))
11033 return false;
11034
303f0be7
JS
11035 // Size
11036
11037 if (!m_size.EqPartial(attr.m_size))
11038 return false;
11039 if (!m_minSize.EqPartial(attr.m_minSize))
11040 return false;
11041 if (!m_maxSize.EqPartial(attr.m_maxSize))
11042 return false;
11043
24777478
JS
11044 // Margins
11045
11046 if (!m_margins.EqPartial(attr.m_margins))
11047 return false;
11048
11049 // Padding
11050
11051 if (!m_padding.EqPartial(attr.m_padding))
11052 return false;
11053
11054 // Border
11055
11056 if (!GetBorder().EqPartial(attr.GetBorder()))
11057 return false;
11058
11059 // Outline
11060
11061 if (!GetOutline().EqPartial(attr.GetOutline()))
11062 return false;
11063
11064 return true;
11065}
11066
11067// Merges the given attributes. If compareWith
11068// is non-NULL, then it will be used to mask out those attributes that are the same in style
11069// and compareWith, for situations where we don't want to explicitly set inherited attributes.
11070bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
11071{
11072 if (attr.HasFloatMode())
11073 {
11074 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
11075 SetFloatMode(attr.GetFloatMode());
11076 }
11077
11078 if (attr.HasClearMode())
11079 {
11080 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
11081 SetClearMode(attr.GetClearMode());
11082 }
11083
11084 if (attr.HasCollapseBorders())
11085 {
11086 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
11087 SetCollapseBorders(attr.GetCollapseBorders());
11088 }
11089
11090 if (attr.HasVerticalAlignment())
11091 {
11092 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
11093 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 11094 }
bec80f4f 11095
2f987d83
JS
11096 if (attr.HasBoxStyleName())
11097 {
11098 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
11099 SetBoxStyleName(attr.GetBoxStyleName());
11100 }
11101
bec80f4f
JS
11102 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
11103 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
11104 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 11105
603f702b 11106 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
11107 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
11108 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 11109
bec80f4f
JS
11110 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
11111 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
11112
11113 return true;
11114}
11115
11116// Remove specified attributes from this object
11117bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
11118{
11119 if (attr.HasFloatMode())
11120 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11121
11122 if (attr.HasClearMode())
11123 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11124
11125 if (attr.HasCollapseBorders())
11126 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11127
603f702b
JS
11128 if (attr.HasVerticalAlignment())
11129 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11130
2f987d83
JS
11131 if (attr.HasBoxStyleName())
11132 {
11133 SetBoxStyleName(wxEmptyString);
11134 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11135 }
11136
24777478
JS
11137 m_margins.RemoveStyle(attr.m_margins);
11138 m_padding.RemoveStyle(attr.m_padding);
11139 m_position.RemoveStyle(attr.m_position);
11140
603f702b 11141 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
11142 m_minSize.RemoveStyle(attr.m_minSize);
11143 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
11144
11145 m_border.RemoveStyle(attr.m_border);
11146 m_outline.RemoveStyle(attr.m_outline);
11147
11148 return true;
11149}
11150
11151// Collects the attributes that are common to a range of content, building up a note of
11152// which attributes are absent in some objects and which clash in some objects.
11153void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
11154{
11155 if (attr.HasFloatMode())
11156 {
11157 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
11158 {
11159 if (HasFloatMode())
11160 {
11161 if (GetFloatMode() != attr.GetFloatMode())
11162 {
11163 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
11164 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11165 }
11166 }
11167 else
11168 SetFloatMode(attr.GetFloatMode());
11169 }
11170 }
11171 else
11172 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 11173
24777478
JS
11174 if (attr.HasClearMode())
11175 {
11176 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
11177 {
11178 if (HasClearMode())
11179 {
11180 if (GetClearMode() != attr.GetClearMode())
11181 {
11182 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11183 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11184 }
11185 }
11186 else
11187 SetClearMode(attr.GetClearMode());
11188 }
11189 }
11190 else
11191 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11192
11193 if (attr.HasCollapseBorders())
11194 {
11195 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
11196 {
11197 if (HasCollapseBorders())
11198 {
11199 if (GetCollapseBorders() != attr.GetCollapseBorders())
11200 {
11201 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11202 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11203 }
11204 }
11205 else
11206 SetCollapseBorders(attr.GetCollapseBorders());
11207 }
11208 }
11209 else
11210 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 11211
603f702b
JS
11212 if (attr.HasVerticalAlignment())
11213 {
11214 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
11215 {
11216 if (HasVerticalAlignment())
11217 {
11218 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
11219 {
11220 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11221 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11222 }
11223 }
11224 else
11225 SetVerticalAlignment(attr.GetVerticalAlignment());
11226 }
11227 }
11228 else
11229 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11230
2f987d83
JS
11231 if (attr.HasBoxStyleName())
11232 {
11233 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
11234 {
11235 if (HasBoxStyleName())
11236 {
11237 if (GetBoxStyleName() != attr.GetBoxStyleName())
11238 {
11239 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11240 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11241 }
11242 }
11243 else
11244 SetBoxStyleName(attr.GetBoxStyleName());
11245 }
11246 }
11247 else
11248 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11249
24777478
JS
11250 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
11251 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
11252 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
11253
603f702b 11254 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
11255 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
11256 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
11257
11258 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
11259 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
11260}
11261
eb3d8a33
JS
11262bool wxTextBoxAttr::IsDefault() const
11263{
11264 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 11265 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
11266 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
11267}
11268
24777478
JS
11269// wxRichTextAttr
11270
11271void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
11272{
bec80f4f
JS
11273 wxTextAttr::Copy(attr);
11274
24777478
JS
11275 m_textBoxAttr = attr.m_textBoxAttr;
11276}
11277
11278bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
11279{
11280 if (!(wxTextAttr::operator==(attr)))
11281 return false;
bec80f4f 11282
24777478
JS
11283 return (m_textBoxAttr == attr.m_textBoxAttr);
11284}
11285
11286// Partial equality test taking comparison object into account
11287bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const
11288{
11289 if (!(wxTextAttr::EqPartial(attr)))
11290 return false;
bec80f4f 11291
24777478
JS
11292 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr);
11293}
11294
11295// Merges the given attributes. If compareWith
11296// is non-NULL, then it will be used to mask out those attributes that are the same in style
11297// and compareWith, for situations where we don't want to explicitly set inherited attributes.
11298bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
11299{
11300 wxTextAttr::Apply(style, compareWith);
11301
11302 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
11303}
11304
11305// Remove specified attributes from this object
11306bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
11307{
11308 wxTextAttr::RemoveStyle(*this, attr);
11309
11310 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
11311}
11312
11313// Collects the attributes that are common to a range of content, building up a note of
11314// which attributes are absent in some objects and which clash in some objects.
11315void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
11316{
11317 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 11318
24777478
JS
11319 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
11320}
11321
11322// Partial equality test
bec80f4f 11323bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border) const
24777478
JS
11324{
11325 if (border.HasStyle() && !HasStyle() && (border.GetStyle() != GetStyle()))
11326 return false;
11327
11328 if (border.HasColour() && !HasColour() && (border.GetColourLong() != GetColourLong()))
11329 return false;
11330
11331 if (border.HasWidth() && !HasWidth() && !(border.GetWidth() == GetWidth()))
11332 return false;
11333
11334 return true;
11335}
11336
11337// Apply border to 'this', but not if the same as compareWith
bec80f4f 11338bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
11339{
11340 if (border.HasStyle())
11341 {
11342 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
11343 SetStyle(border.GetStyle());
11344 }
11345 if (border.HasColour())
11346 {
11347 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
11348 SetColour(border.GetColourLong());
11349 }
11350 if (border.HasWidth())
11351 {
11352 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
11353 SetWidth(border.GetWidth());
11354 }
11355
11356 return true;
11357}
11358
11359// Remove specified attributes from this object
bec80f4f 11360bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
11361{
11362 if (attr.HasStyle() && HasStyle())
11363 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
11364 if (attr.HasColour() && HasColour())
11365 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
11366 if (attr.HasWidth() && HasWidth())
11367 m_borderWidth.Reset();
11368
11369 return true;
11370}
11371
11372// Collects the attributes that are common to a range of content, building up a note of
11373// which attributes are absent in some objects and which clash in some objects.
bec80f4f 11374void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
11375{
11376 if (attr.HasStyle())
11377 {
11378 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
11379 {
11380 if (HasStyle())
11381 {
11382 if (GetStyle() != attr.GetStyle())
11383 {
11384 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11385 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11386 }
11387 }
11388 else
11389 SetStyle(attr.GetStyle());
11390 }
11391 }
11392 else
11393 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11394
11395 if (attr.HasColour())
11396 {
11397 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
11398 {
11399 if (HasColour())
11400 {
11401 if (GetColour() != attr.GetColour())
11402 {
11403 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
11404 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
11405 }
11406 }
11407 else
11408 SetColour(attr.GetColourLong());
11409 }
11410 }
11411 else
11412 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 11413
24777478
JS
11414 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
11415}
11416
11417// Partial equality test
bec80f4f 11418bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders) const
24777478
JS
11419{
11420 return m_left.EqPartial(borders.m_left) && m_right.EqPartial(borders.m_right) &&
11421 m_top.EqPartial(borders.m_top) && m_bottom.EqPartial(borders.m_bottom);
11422}
11423
11424// Apply border to 'this', but not if the same as compareWith
bec80f4f 11425bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 11426{
bec80f4f
JS
11427 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
11428 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
11429 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
11430 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
11431 return true;
11432}
11433
11434// Remove specified attributes from this object
bec80f4f 11435bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
11436{
11437 m_left.RemoveStyle(attr.m_left);
11438 m_right.RemoveStyle(attr.m_right);
11439 m_top.RemoveStyle(attr.m_top);
11440 m_bottom.RemoveStyle(attr.m_bottom);
11441 return true;
11442}
11443
11444// Collects the attributes that are common to a range of content, building up a note of
11445// which attributes are absent in some objects and which clash in some objects.
bec80f4f 11446void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
11447{
11448 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
11449 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
11450 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
11451 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
11452}
11453
11454// Set style of all borders
bec80f4f 11455void wxTextAttrBorders::SetStyle(int style)
24777478
JS
11456{
11457 m_left.SetStyle(style);
11458 m_right.SetStyle(style);
11459 m_top.SetStyle(style);
11460 m_bottom.SetStyle(style);
11461}
11462
11463// Set colour of all borders
bec80f4f 11464void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
11465{
11466 m_left.SetColour(colour);
11467 m_right.SetColour(colour);
11468 m_top.SetColour(colour);
11469 m_bottom.SetColour(colour);
11470}
11471
bec80f4f 11472void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
11473{
11474 m_left.SetColour(colour);
11475 m_right.SetColour(colour);
11476 m_top.SetColour(colour);
11477 m_bottom.SetColour(colour);
11478}
11479
11480// Set width of all borders
bec80f4f 11481void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
11482{
11483 m_left.SetWidth(width);
11484 m_right.SetWidth(width);
11485 m_top.SetWidth(width);
11486 m_bottom.SetWidth(width);
11487}
11488
11489// Partial equality test
11490bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim) const
11491{
603f702b 11492 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
11493 return false;
11494 else
11495 return true;
11496}
11497
11498bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
11499{
603f702b 11500 if (dim.IsValid())
24777478
JS
11501 {
11502 if (!(compareWith && dim == (*compareWith)))
11503 (*this) = dim;
11504 }
11505
11506 return true;
11507}
11508
11509// Collects the attributes that are common to a range of content, building up a note of
11510// which attributes are absent in some objects and which clash in some objects.
11511void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
11512{
603f702b 11513 if (attr.IsValid())
24777478 11514 {
603f702b 11515 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 11516 {
603f702b 11517 if (IsValid())
24777478
JS
11518 {
11519 if (!((*this) == attr))
11520 {
603f702b
JS
11521 clashingAttr.SetValid(true);
11522 SetValid(false);
24777478
JS
11523 }
11524 }
11525 else
11526 (*this) = attr;
11527 }
11528 }
11529 else
603f702b 11530 absentAttr.SetValid(true);
24777478
JS
11531}
11532
8995db52
JS
11533wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
11534{
11535 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
11536}
11537
11538wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
11539{
11540 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
11541}
11542
bec80f4f
JS
11543int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
11544{
11545 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
11546}
11547
11548int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
11549{
11550 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
11551}
11552
11553int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
11554{
11555 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11556 return ConvertTenthsMMToPixels(dim.GetValue());
11557 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11558 return dim.GetValue();
11559 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11560 {
11561 wxASSERT(m_parentSize != wxDefaultSize);
11562 if (direction == wxHORIZONTAL)
11563 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
11564 else
11565 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
11566 }
11567 else
11568 {
11569 wxASSERT(false);
11570 return 0;
11571 }
11572}
11573
11574int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
11575{
11576 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11577 return dim.GetValue();
11578 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11579 return ConvertPixelsToTenthsMM(dim.GetValue());
11580 else
11581 {
11582 wxASSERT(false);
11583 return 0;
11584 }
11585}
11586
24777478 11587// Partial equality test
bec80f4f 11588bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims) const
24777478
JS
11589{
11590 if (!m_left.EqPartial(dims.m_left))
11591 return false;
11592
11593 if (!m_right.EqPartial(dims.m_right))
11594 return false;
11595
11596 if (!m_top.EqPartial(dims.m_top))
11597 return false;
11598
11599 if (!m_bottom.EqPartial(dims.m_bottom))
11600 return false;
11601
11602 return true;
11603}
11604
11605// Apply border to 'this', but not if the same as compareWith
bec80f4f 11606bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
11607{
11608 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
11609 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
11610 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
11611 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
11612
11613 return true;
11614}
11615
11616// Remove specified attributes from this object
bec80f4f 11617bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 11618{
603f702b 11619 if (attr.m_left.IsValid())
24777478 11620 m_left.Reset();
603f702b 11621 if (attr.m_right.IsValid())
24777478 11622 m_right.Reset();
603f702b 11623 if (attr.m_top.IsValid())
24777478 11624 m_top.Reset();
603f702b 11625 if (attr.m_bottom.IsValid())
24777478
JS
11626 m_bottom.Reset();
11627
11628 return true;
11629}
11630
11631// Collects the attributes that are common to a range of content, building up a note of
11632// which attributes are absent in some objects and which clash in some objects.
bec80f4f 11633void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
11634{
11635 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
11636 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
11637 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
11638 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
11639}
11640
603f702b
JS
11641// Partial equality test
11642bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size) const
11643{
11644 if (!m_width.EqPartial(size.m_width))
11645 return false;
11646
11647 if (!m_height.EqPartial(size.m_height))
11648 return false;
11649
11650 return true;
11651}
11652
11653// Apply border to 'this', but not if the same as compareWith
11654bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
11655{
11656 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
11657 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
11658
11659 return true;
11660}
11661
11662// Remove specified attributes from this object
11663bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
11664{
11665 if (attr.m_width.IsValid())
11666 m_width.Reset();
11667 if (attr.m_height.IsValid())
11668 m_height.Reset();
11669
11670 return true;
11671}
11672
11673// Collects the attributes that are common to a range of content, building up a note of
11674// which attributes are absent in some objects and which clash in some objects.
11675void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
11676{
11677 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
11678 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
11679}
11680
24777478
JS
11681// Collects the attributes that are common to a range of content, building up a note of
11682// which attributes are absent in some objects and which clash in some objects.
11683void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
11684{
11685 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
11686 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
11687
11688 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
11689
11690 if (attr.HasFont())
11691 {
11692 if (attr.HasFontSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_SIZE))
11693 {
11694 if (currentStyle.HasFontSize())
11695 {
11696 if (currentStyle.GetFontSize() != attr.GetFontSize())
11697 {
11698 // Clash of attr - mark as such
11699 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
11700 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
11701 }
11702 }
11703 else
11704 currentStyle.SetFontSize(attr.GetFontSize());
11705 }
11706
11707 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
11708 {
11709 if (currentStyle.HasFontItalic())
11710 {
11711 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
11712 {
11713 // Clash of attr - mark as such
11714 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
11715 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
11716 }
11717 }
11718 else
11719 currentStyle.SetFontStyle(attr.GetFontStyle());
11720 }
11721
11722 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
11723 {
11724 if (currentStyle.HasFontFamily())
11725 {
11726 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
11727 {
11728 // Clash of attr - mark as such
11729 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
11730 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
11731 }
11732 }
11733 else
11734 currentStyle.SetFontFamily(attr.GetFontFamily());
11735 }
11736
11737 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
11738 {
11739 if (currentStyle.HasFontWeight())
11740 {
11741 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
11742 {
11743 // Clash of attr - mark as such
11744 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
11745 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
11746 }
11747 }
11748 else
11749 currentStyle.SetFontWeight(attr.GetFontWeight());
11750 }
11751
11752 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
11753 {
11754 if (currentStyle.HasFontFaceName())
11755 {
11756 wxString faceName1(currentStyle.GetFontFaceName());
11757 wxString faceName2(attr.GetFontFaceName());
11758
11759 if (faceName1 != faceName2)
11760 {
11761 // Clash of attr - mark as such
11762 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
11763 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
11764 }
11765 }
11766 else
11767 currentStyle.SetFontFaceName(attr.GetFontFaceName());
11768 }
11769
11770 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
11771 {
11772 if (currentStyle.HasFontUnderlined())
11773 {
11774 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
11775 {
11776 // Clash of attr - mark as such
11777 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
11778 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
11779 }
11780 }
11781 else
11782 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
11783 }
11784 }
11785
11786 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
11787 {
11788 if (currentStyle.HasTextColour())
11789 {
11790 if (currentStyle.GetTextColour() != attr.GetTextColour())
11791 {
11792 // Clash of attr - mark as such
11793 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
11794 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
11795 }
11796 }
11797 else
11798 currentStyle.SetTextColour(attr.GetTextColour());
11799 }
11800
11801 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
11802 {
11803 if (currentStyle.HasBackgroundColour())
11804 {
11805 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
11806 {
11807 // Clash of attr - mark as such
11808 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
11809 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
11810 }
11811 }
11812 else
11813 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
11814 }
11815
11816 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
11817 {
11818 if (currentStyle.HasAlignment())
11819 {
11820 if (currentStyle.GetAlignment() != attr.GetAlignment())
11821 {
11822 // Clash of attr - mark as such
11823 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
11824 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
11825 }
11826 }
11827 else
11828 currentStyle.SetAlignment(attr.GetAlignment());
11829 }
11830
11831 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
11832 {
11833 if (currentStyle.HasTabs())
11834 {
11835 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
11836 {
11837 // Clash of attr - mark as such
11838 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
11839 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
11840 }
11841 }
11842 else
11843 currentStyle.SetTabs(attr.GetTabs());
11844 }
11845
11846 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
11847 {
11848 if (currentStyle.HasLeftIndent())
11849 {
11850 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
11851 {
11852 // Clash of attr - mark as such
11853 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
11854 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
11855 }
11856 }
11857 else
11858 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
11859 }
11860
11861 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
11862 {
11863 if (currentStyle.HasRightIndent())
11864 {
11865 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
11866 {
11867 // Clash of attr - mark as such
11868 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
11869 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
11870 }
11871 }
11872 else
11873 currentStyle.SetRightIndent(attr.GetRightIndent());
11874 }
11875
11876 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
11877 {
11878 if (currentStyle.HasParagraphSpacingAfter())
11879 {
11880 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
11881 {
11882 // Clash of attr - mark as such
11883 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
11884 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
11885 }
11886 }
11887 else
11888 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
11889 }
11890
11891 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
11892 {
11893 if (currentStyle.HasParagraphSpacingBefore())
11894 {
11895 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
11896 {
11897 // Clash of attr - mark as such
11898 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
11899 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
11900 }
11901 }
11902 else
11903 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
11904 }
11905
11906 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
11907 {
11908 if (currentStyle.HasLineSpacing())
11909 {
11910 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
11911 {
11912 // Clash of attr - mark as such
11913 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
11914 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
11915 }
11916 }
11917 else
11918 currentStyle.SetLineSpacing(attr.GetLineSpacing());
11919 }
11920
11921 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
11922 {
11923 if (currentStyle.HasCharacterStyleName())
11924 {
11925 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
11926 {
11927 // Clash of attr - mark as such
11928 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
11929 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
11930 }
11931 }
11932 else
11933 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
11934 }
11935
11936 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
11937 {
11938 if (currentStyle.HasParagraphStyleName())
11939 {
11940 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
11941 {
11942 // Clash of attr - mark as such
11943 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
11944 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
11945 }
11946 }
11947 else
11948 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
11949 }
11950
11951 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
11952 {
11953 if (currentStyle.HasListStyleName())
11954 {
11955 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
11956 {
11957 // Clash of attr - mark as such
11958 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
11959 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
11960 }
11961 }
11962 else
11963 currentStyle.SetListStyleName(attr.GetListStyleName());
11964 }
11965
11966 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
11967 {
11968 if (currentStyle.HasBulletStyle())
11969 {
11970 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
11971 {
11972 // Clash of attr - mark as such
11973 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
11974 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
11975 }
11976 }
11977 else
11978 currentStyle.SetBulletStyle(attr.GetBulletStyle());
11979 }
11980
11981 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
11982 {
11983 if (currentStyle.HasBulletNumber())
11984 {
11985 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
11986 {
11987 // Clash of attr - mark as such
11988 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
11989 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
11990 }
11991 }
11992 else
11993 currentStyle.SetBulletNumber(attr.GetBulletNumber());
11994 }
11995
11996 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
11997 {
11998 if (currentStyle.HasBulletText())
11999 {
12000 if (currentStyle.GetBulletText() != attr.GetBulletText())
12001 {
12002 // Clash of attr - mark as such
12003 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
12004 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
12005 }
12006 }
12007 else
12008 {
12009 currentStyle.SetBulletText(attr.GetBulletText());
12010 currentStyle.SetBulletFont(attr.GetBulletFont());
12011 }
12012 }
12013
12014 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
12015 {
12016 if (currentStyle.HasBulletName())
12017 {
12018 if (currentStyle.GetBulletName() != attr.GetBulletName())
12019 {
12020 // Clash of attr - mark as such
12021 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
12022 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
12023 }
12024 }
12025 else
12026 {
12027 currentStyle.SetBulletName(attr.GetBulletName());
12028 }
12029 }
12030
12031 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
12032 {
12033 if (currentStyle.HasURL())
12034 {
12035 if (currentStyle.GetURL() != attr.GetURL())
12036 {
12037 // Clash of attr - mark as such
12038 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
12039 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
12040 }
12041 }
12042 else
12043 {
12044 currentStyle.SetURL(attr.GetURL());
12045 }
12046 }
12047
12048 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
12049 {
12050 if (currentStyle.HasTextEffects())
12051 {
12052 // We need to find the bits in the new attr that are different:
12053 // just look at those bits that are specified by the new attr.
12054
12055 // We need to remove the bits and flags that are not common between current attr
12056 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12057 // previous styles.
12058
12059 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
12060 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
12061
12062 if (currentRelevantTextEffects != newRelevantTextEffects)
12063 {
12064 // Find the text effects that were different, using XOR
12065 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
12066
12067 // Clash of attr - mark as such
12068 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
12069 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
12070 }
12071 }
12072 else
12073 {
12074 currentStyle.SetTextEffects(attr.GetTextEffects());
12075 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
12076 }
12077
12078 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12079 // that we've looked at so far
12080 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
12081 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
12082
12083 if (currentStyle.GetTextEffectFlags() == 0)
12084 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
12085 }
12086
12087 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
12088 {
12089 if (currentStyle.HasOutlineLevel())
12090 {
12091 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
12092 {
12093 // Clash of attr - mark as such
12094 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12095 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12096 }
12097 }
12098 else
12099 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
12100 }
12101}
12102
bec80f4f
JS
12103WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
12104
12105IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
12106
12107bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
12108{
12109 if (m_properties.GetCount() != props.GetCount())
12110 return false;
12111
12112 size_t i;
12113 for (i = 0; i < m_properties.GetCount(); i++)
12114 {
12115 const wxVariant& var1 = m_properties[i];
12116 int idx = props.Find(var1.GetName());
12117 if (idx == -1)
12118 return false;
12119 const wxVariant& var2 = props.m_properties[idx];
12120 if (!(var1 == var2))
12121 return false;
12122 }
12123
12124 return true;
12125}
12126
12127wxArrayString wxRichTextProperties::GetPropertyNames() const
12128{
12129 wxArrayString arr;
12130 size_t i;
12131 for (i = 0; i < m_properties.GetCount(); i++)
12132 {
12133 arr.Add(m_properties[i].GetName());
12134 }
12135 return arr;
12136}
12137
12138int wxRichTextProperties::Find(const wxString& name) const
12139{
12140 size_t i;
12141 for (i = 0; i < m_properties.GetCount(); i++)
12142 {
12143 if (m_properties[i].GetName() == name)
12144 return (int) i;
12145 }
12146 return -1;
12147}
12148
590a0f8b
JS
12149bool wxRichTextProperties::Remove(const wxString& name)
12150{
12151 int idx = Find(name);
12152 if (idx != -1)
12153 {
12154 m_properties.RemoveAt(idx);
12155 return true;
12156 }
12157 else
12158 return false;
12159}
12160
bec80f4f
JS
12161wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
12162{
12163 int idx = Find(name);
12164 if (idx == wxNOT_FOUND)
12165 SetProperty(name, wxString());
12166 idx = Find(name);
12167 if (idx != wxNOT_FOUND)
12168 {
12169 return & (*this)[idx];
12170 }
12171 else
12172 return NULL;
12173}
12174
12175const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
12176{
12177 static const wxVariant nullVariant;
12178 int idx = Find(name);
12179 if (idx != -1)
12180 return m_properties[idx];
12181 else
12182 return nullVariant;
12183}
12184
12185wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
12186{
12187 return GetProperty(name).GetString();
12188}
12189
12190long wxRichTextProperties::GetPropertyLong(const wxString& name) const
12191{
12192 return GetProperty(name).GetLong();
12193}
12194
12195bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
12196{
12197 return GetProperty(name).GetBool();
12198}
12199
12200double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
12201{
12202 return GetProperty(name).GetDouble();
12203}
12204
12205void wxRichTextProperties::SetProperty(const wxVariant& variant)
12206{
12207 wxASSERT(!variant.GetName().IsEmpty());
12208
12209 int idx = Find(variant.GetName());
12210
12211 if (idx == -1)
12212 m_properties.Add(variant);
12213 else
12214 m_properties[idx] = variant;
12215}
12216
12217void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
12218{
12219 int idx = Find(name);
12220 wxVariant var(variant);
12221 var.SetName(name);
12222
12223 if (idx == -1)
12224 m_properties.Add(var);
12225 else
12226 m_properties[idx] = var;
12227}
12228
12229void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
12230{
12231 SetProperty(name, wxVariant(value, name));
12232}
12233
12234void wxRichTextProperties::SetProperty(const wxString& name, long value)
12235{
12236 SetProperty(name, wxVariant(value, name));
12237}
12238
12239void wxRichTextProperties::SetProperty(const wxString& name, double value)
12240{
12241 SetProperty(name, wxVariant(value, name));
12242}
12243
12244void wxRichTextProperties::SetProperty(const wxString& name, bool value)
12245{
12246 SetProperty(name, wxVariant(value, name));
12247}
24777478 12248
590a0f8b
JS
12249void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
12250{
12251 size_t i;
12252 for (i = 0; i < properties.GetCount(); i++)
12253 {
12254 wxString name = properties.GetProperties()[i].GetName();
12255 if (HasProperty(name))
12256 Remove(name);
12257 }
12258}
12259
12260void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
12261{
12262 size_t i;
12263 for (i = 0; i < properties.GetCount(); i++)
12264 {
12265 SetProperty(properties.GetProperties()[i]);
12266 }
12267}
12268
603f702b
JS
12269wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
12270{
12271 if (m_address.GetCount() == 0)
12272 return topLevelContainer;
12273
12274 wxRichTextCompositeObject* p = topLevelContainer;
12275 size_t i = 0;
12276 while (p && i < m_address.GetCount())
12277 {
12278 int pos = m_address[i];
12279 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
12280 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
12281 return NULL;
12282
12283 wxRichTextObject* p1 = p->GetChild(pos);
12284 if (i == (m_address.GetCount()-1))
12285 return p1;
12286
12287 p = wxDynamicCast(p1, wxRichTextCompositeObject);
12288 i ++;
12289 }
12290 return NULL;
12291}
12292
12293bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
12294{
12295 m_address.Clear();
12296
12297 if (topLevelContainer == obj)
12298 return true;
12299
12300 wxRichTextObject* o = obj;
12301 while (o)
12302 {
12303 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
12304 if (!p)
12305 return false;
12306
12307 int pos = p->GetChildren().IndexOf(o);
12308 if (pos == -1)
12309 return false;
12310
12311 m_address.Insert(pos, 0);
12312
12313 if (p == topLevelContainer)
12314 return true;
12315
12316 o = p;
12317 }
12318 return false;
12319}
12320
12321// Equality test
12322bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
12323{
12324 if (m_container != sel.m_container)
12325 return false;
12326 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
12327 return false;
12328 size_t i;
12329 for (i = 0; i < m_ranges.GetCount(); i++)
12330 if (!(m_ranges[i] == sel.m_ranges[i]))
12331 return false;
12332 return true;
12333}
12334
12335// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12336// or none at the level of the object's container.
12337wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
12338{
12339 if (IsValid())
12340 {
12341 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
12342
12343 if (container == m_container)
12344 return m_ranges;
12345
12346 container = obj->GetContainer();
12347 while (container)
12348 {
12349 if (container->GetParent())
12350 {
12351 // If we found that our object's container is within the range of
12352 // a selection higher up, then assume the whole original object
12353 // is also selected.
12354 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
12355 if (parentContainer == m_container)
12356 {
12357 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
12358 {
12359 wxRichTextRangeArray ranges;
12360 ranges.Add(obj->GetRange());
12361 return ranges;
12362 }
12363 }
12364
12365 container = parentContainer;
12366 }
12367 else
12368 {
12369 container = NULL;
12370 break;
12371 }
12372 }
12373 }
12374 return wxRichTextRangeArray();
12375}
12376
12377// Is the given position within the selection?
12378bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
12379{
12380 if (!IsValid())
12381 return false;
12382 else
12383 {
12384 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
12385 return WithinSelection(pos, selectionRanges);
12386 }
12387}
12388
12389// Is the given position within the selection range?
12390bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
12391{
12392 size_t i;
12393 for (i = 0; i < ranges.GetCount(); i++)
12394 {
12395 const wxRichTextRange& range = ranges[i];
12396 if (pos >= range.GetStart() && pos <= range.GetEnd())
12397 return true;
12398 }
12399 return false;
12400}
12401
12402// Is the given range completely within the selection range?
12403bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
12404{
12405 size_t i;
12406 for (i = 0; i < ranges.GetCount(); i++)
12407 {
12408 const wxRichTextRange& eachRange = ranges[i];
12409 if (range.IsWithin(eachRange))
12410 return true;
12411 }
12412 return false;
12413}
12414
8db2e3ef
JS
12415IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
12416IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
12417
12418bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
12419{
12420 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
12421 while (node)
12422 {
12423 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
12424 if (handler->HasVirtualAttributes(obj))
12425 return true;
12426
12427 node = node->GetNext();
12428 }
12429 return false;
12430}
12431
12432wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
12433{
12434 wxRichTextAttr attr;
12435 // We apply all handlers, so we can may combine several different attributes
12436 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
12437 while (node)
12438 {
12439 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
12440 if (handler->HasVirtualAttributes(obj))
12441 {
12442 bool success = handler->GetVirtualAttributes(attr, obj);
12443 wxASSERT(success);
aa8f57f4 12444 wxUnusedVar(success);
8db2e3ef
JS
12445 }
12446
12447 node = node->GetNext();
12448 }
12449 return attr;
12450}
12451
12452bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
12453{
12454 if (HasVirtualAttributes(obj))
12455 {
12456 wxRichTextAttr a(GetVirtualAttributes(obj));
12457 attr.Apply(a);
12458 return true;
12459 }
12460 else
12461 return false;
12462}
12463
12464/// Adds a handler to the end
12465void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
12466{
12467 sm_drawingHandlers.Append(handler);
12468}
12469
12470/// Inserts a handler at the front
12471void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
12472{
12473 sm_drawingHandlers.Insert( handler );
12474}
12475
12476/// Removes a handler
12477bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
12478{
12479 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
12480 if (handler)
12481 {
12482 sm_drawingHandlers.DeleteObject(handler);
12483 delete handler;
12484 return true;
12485 }
12486 else
12487 return false;
12488}
12489
12490wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
12491{
12492 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
12493 while (node)
12494 {
12495 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
12496 if (handler->GetName().Lower() == name.Lower()) return handler;
12497
12498 node = node->GetNext();
12499 }
12500 return NULL;
12501}
12502
12503void wxRichTextBuffer::CleanUpDrawingHandlers()
12504{
12505 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
12506 while (node)
12507 {
12508 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
12509 wxList::compatibility_iterator next = node->GetNext();
12510 delete handler;
12511 node = next;
12512 }
12513
12514 sm_drawingHandlers.Clear();
12515}
603f702b 12516
5d7836c4
JS
12517#endif
12518 // wxUSE_RICHTEXT