]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
Changed wxMSW wxGraphicsContext font rendering and extent calculation to take into...
[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
603f702b 108 void Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652
VZ
109
110 // HitTest the floats
603f702b
JS
111 int HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
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
VZ
118
119 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
120
121 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
ce00f59b 122
cdaed652
VZ
123 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
124
603f702b 125 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652 126
603f702b 127 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
ce00f59b 128
cdaed652
VZ
129private:
130 wxRichTextFloatRectMapArray m_left;
131 wxRichTextFloatRectMapArray m_right;
603f702b
JS
132 //int m_width;
133 wxRect m_availableRect;
cdaed652
VZ
134 wxRichTextParagraph* m_para;
135};
136
603f702b
JS
137// Get floating objects
138bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
139{
140 size_t i;
141 for (i = 0; i < m_left.GetCount(); i++)
142 objects.Append(m_left[i]->anchor);
143 for (i = 0; i < m_right.GetCount(); i++)
144 objects.Append(m_right[i]->anchor);
145 return true;
146}
147
148
cdaed652
VZ
149/*
150 * Binary search helper function
151 * The argument point is the Y coordinate, and this fuction
152 * always return the floating rect that contain this coordinate
153 * or under this coordinate.
154 */
155int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
156{
157 int end = array.GetCount() - 1;
158 int start = 0;
159 int ret = 0;
160
161 wxASSERT(end >= 0);
162
163 while (true)
164 {
165 if (start > end)
166 {
167 break;
168 }
169
170 int mid = (start + end) / 2;
171 if (array[mid]->startY <= point && array[mid]->endY >= point)
172 return mid;
173 else if (array[mid]->startY > point)
174 {
175 end = mid - 1;
176 ret = mid;
177 }
178 else if (array[mid]->endY < point)
179 {
180 start = mid + 1;
181 ret = start;
182 }
183 }
184
185 return ret;
186}
187
188int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
189{
190 int ret = 0;
191 int len = array.GetCount();
192
193 wxASSERT(index >= 0 && index < len);
194
195 if (array[index]->startY < startY && array[index]->endY > startY)
196 ret = ret < array[index]->width ? array[index]->width : ret;
197 while (index < len && array[index]->startY <= endY)
198 {
199 ret = ret < array[index]->width ? array[index]->width : ret;
200 index++;
201 }
202
203 return ret;
204}
205
603f702b 206wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
cdaed652 207{
603f702b 208 m_availableRect = rect;
cdaed652
VZ
209 m_para = NULL;
210}
211
212void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
213{
214 int len = array.GetCount();
215 for (int i = 0; i < len; i++)
216 delete array[i];
217}
218
219wxRichTextFloatCollector::~wxRichTextFloatCollector()
220{
221 FreeFloatRectMapArray(m_left);
222 FreeFloatRectMapArray(m_right);
223}
224
225int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
226{
227 if (array.GetCount() == 0)
228 return start;
229
603f702b 230 int i = SearchAdjacentRect(array, start);
cdaed652 231 int last = start;
603f702b 232 while (i < (int) array.GetCount())
cdaed652
VZ
233 {
234 if (array[i]->startY - last >= height)
235 return last + 1;
236 last = array[i]->endY;
237 i++;
238 }
239
240 return last + 1;
241}
242
243int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
244{
24777478 245 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
cdaed652 246 return GetFitPosition(m_left, start, height);
24777478 247 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
cdaed652
VZ
248 return GetFitPosition(m_right, start, height);
249 else
250 {
251 wxASSERT("Never should be here");
252 return start;
253 }
254}
255
603f702b
JS
256// Adds a floating image to the float collector.
257// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
cdaed652
VZ
258void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
259{
603f702b 260 int direction = floating->GetFloatDirection();
24777478 261
603f702b
JS
262 wxPoint pos = floating->GetPosition();
263 wxSize size = floating->GetCachedSize();
264 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
265 switch (direction)
266 {
267 case wxTEXT_BOX_ATTR_FLOAT_NONE:
268 delete map;
269 break;
270 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
271 // Just a not-enough simple assertion
272 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
273 m_left.Add(map);
274 break;
275 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
276 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
277 m_right.Add(map);
278 break;
279 default:
280 delete map;
281 wxASSERT("Unrecognised float attribute.");
282 }
cdaed652 283
603f702b 284 m_para = para;
cdaed652
VZ
285}
286
287void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
288{
289 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
290 while (node)
291 {
292 wxRichTextObject* floating = node->GetData();
ce00f59b 293
cdaed652
VZ
294 if (floating->IsFloating())
295 {
bec80f4f 296 CollectFloat(para, floating);
cdaed652 297 }
ce00f59b 298
cdaed652
VZ
299 node = node->GetNext();
300 }
301
302 m_para = para;
303}
304
305wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
306{
307 return m_para;
308}
309
310wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
311{
312 int widthLeft = 0, widthRight = 0;
313 if (m_left.GetCount() != 0)
314 {
603f702b
JS
315 int i = SearchAdjacentRect(m_left, startY);
316 if (i < (int) m_left.GetCount())
cdaed652
VZ
317 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
318 }
319 if (m_right.GetCount() != 0)
320 {
603f702b
JS
321 int j = SearchAdjacentRect(m_right, startY);
322 if (j < (int) m_right.GetCount())
cdaed652
VZ
323 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
324 }
325
603f702b
JS
326 // TODO: actually we want to use the actual image positions to find the
327 // available remaining space, since the image might not be right up against
328 // the left or right edge of the container.
329 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
cdaed652
VZ
330}
331
332int wxRichTextFloatCollector::GetLastRectBottom()
333{
334 int ret = 0;
335 int len = m_left.GetCount();
336 if (len) {
337 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
338 }
339 len = m_right.GetCount();
340 if (len) {
341 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
342 }
343
344 return ret;
345}
346
603f702b 347void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
348{
349 int start = rect.y;
350 int end = rect.y + rect.height;
603f702b 351 int i, j;
cdaed652 352 i = SearchAdjacentRect(array, start);
603f702b 353 if (i < 0 || i >= (int) array.GetCount())
cdaed652
VZ
354 return;
355 j = SearchAdjacentRect(array, end);
603f702b 356 if (j < 0 || j >= (int) array.GetCount())
cdaed652
VZ
357 j = array.GetCount() - 1;
358 while (i <= j)
359 {
360 wxRichTextObject* obj = array[i]->anchor;
361 wxRichTextRange r = obj->GetRange();
603f702b 362 obj->Draw(dc, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
cdaed652
VZ
363 i++;
364 }
365}
ecb5fbf1 366
603f702b 367void wxRichTextFloatCollector::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
368{
369 if (m_left.GetCount() > 0)
603f702b 370 DrawFloat(m_left, dc, range, selection, rect, descent, style);
cdaed652 371 if (m_right.GetCount() > 0)
603f702b 372 DrawFloat(m_right, dc, range, selection, rect, descent, style);
cdaed652
VZ
373}
374
603f702b 375int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
cdaed652 376{
603f702b 377 int i;
cdaed652
VZ
378 if (array.GetCount() == 0)
379 return wxRICHTEXT_HITTEST_NONE;
380 i = SearchAdjacentRect(array, pt.y);
603f702b 381 if (i < 0 || i >= (int) array.GetCount())
cdaed652 382 return wxRICHTEXT_HITTEST_NONE;
603f702b
JS
383 if (!array[i]->anchor->IsShown())
384 return wxRICHTEXT_HITTEST_NONE;
385
cdaed652
VZ
386 wxPoint point = array[i]->anchor->GetPosition();
387 wxSize size = array[i]->anchor->GetCachedSize();
388 if (point.x <= pt.x && point.x + size.x >= pt.x
389 && point.y <= pt.y && point.y + size.y >= pt.y)
390 {
391 textPosition = array[i]->anchor->GetRange().GetStart();
603f702b 392 * obj = array[i]->anchor;
cdaed652
VZ
393 if (pt.x > (pt.x + pt.x + size.x) / 2)
394 return wxRICHTEXT_HITTEST_BEFORE;
395 else
396 return wxRICHTEXT_HITTEST_AFTER;
397 }
ce00f59b 398
cdaed652
VZ
399 return wxRICHTEXT_HITTEST_NONE;
400}
401
603f702b 402int wxRichTextFloatCollector::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
cdaed652 403{
603f702b 404 int ret = HitTestFloat(m_left, dc, pt, textPosition, obj, flags);
cdaed652
VZ
405 if (ret == wxRICHTEXT_HITTEST_NONE)
406 {
603f702b 407 ret = HitTestFloat(m_right, dc, pt, textPosition, obj, flags);
cdaed652
VZ
408 }
409 return ret;
410}
411
ce00f59b 412// Helpers for efficiency
ecb5fbf1
JS
413inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
414{
cdaed652 415 // JACS: did I do this some time ago when testing? Should we re-enable it?
5cb0b827 416#if 0
ecb5fbf1
JS
417 const wxFont& font1 = dc.GetFont();
418 if (font1.IsOk() && font.IsOk())
419 {
420 if (font1.GetPointSize() == font.GetPointSize() &&
421 font1.GetFamily() == font.GetFamily() &&
422 font1.GetStyle() == font.GetStyle() &&
423 font1.GetWeight() == font.GetWeight() &&
3c8766f7 424 font1.GetUnderlined() == font.GetUnderlined() &&
9c4cb611 425 font1.GetFamily() == font.GetFamily() &&
ecb5fbf1
JS
426 font1.GetFaceName() == font.GetFaceName())
427 return;
428 }
5cb0b827 429#endif
ecb5fbf1
JS
430 dc.SetFont(font);
431}
432
433inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
434{
435 const wxPen& pen1 = dc.GetPen();
436 if (pen1.IsOk() && pen.IsOk())
437 {
438 if (pen1.GetWidth() == pen.GetWidth() &&
439 pen1.GetStyle() == pen.GetStyle() &&
440 pen1.GetColour() == pen.GetColour())
441 return;
442 }
443 dc.SetPen(pen);
444}
445
446inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
447{
448 const wxBrush& brush1 = dc.GetBrush();
449 if (brush1.IsOk() && brush.IsOk())
450 {
451 if (brush1.GetStyle() == brush.GetStyle() &&
452 brush1.GetColour() == brush.GetColour())
453 return;
454 }
455 dc.SetBrush(brush);
456}
457
5d7836c4
JS
458/*!
459 * wxRichTextObject
460 * This is the base for drawable objects.
461 */
462
463IMPLEMENT_CLASS(wxRichTextObject, wxObject)
464
465wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
466{
5d7836c4
JS
467 m_refCount = 1;
468 m_parent = parent;
5d7836c4 469 m_descent = 0;
603f702b 470 m_show = true;
5d7836c4
JS
471}
472
473wxRichTextObject::~wxRichTextObject()
474{
475}
476
477void wxRichTextObject::Dereference()
478{
479 m_refCount --;
480 if (m_refCount <= 0)
481 delete this;
482}
483
484/// Copy
485void wxRichTextObject::Copy(const wxRichTextObject& obj)
486{
487 m_size = obj.m_size;
603f702b
JS
488 m_maxSize = obj.m_maxSize;
489 m_minSize = obj.m_minSize;
5d7836c4 490 m_pos = obj.m_pos;
5d7836c4 491 m_range = obj.m_range;
603f702b 492 m_ownRange = obj.m_ownRange;
5d7836c4 493 m_attributes = obj.m_attributes;
bec80f4f 494 m_properties = obj.m_properties;
5d7836c4 495 m_descent = obj.m_descent;
603f702b
JS
496 m_show = obj.m_show;
497}
498
499// Get/set the top-level container of this object.
500wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
501{
502 const wxRichTextObject* p = this;
503 while (p)
504 {
505 if (p->IsTopLevel())
506 {
507 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
508 }
509 p = p->GetParent();
510 }
511 return NULL;
5d7836c4
JS
512}
513
514void wxRichTextObject::SetMargins(int margin)
515{
603f702b 516 SetMargins(margin, margin, margin, margin);
5d7836c4
JS
517}
518
519void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
520{
603f702b
JS
521 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
522 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
523 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
524 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
525}
526
527int wxRichTextObject::GetLeftMargin() const
528{
529 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
530}
531
532int wxRichTextObject::GetRightMargin() const
533{
534 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
535}
536
537int wxRichTextObject::GetTopMargin() const
538{
539 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
540}
541
542int wxRichTextObject::GetBottomMargin() const
543{
544 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
545}
546
547// Calculate the available content space in the given rectangle, given the
548// margins, border and padding specified in the object's attributes.
549wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, const wxRect& outerRect) const
550{
551 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
552 marginRect = outerRect;
553 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
554 return contentRect;
555}
556
557// Invalidate the buffer. With no argument, invalidates whole buffer.
558void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
559{
560 if (invalidRange != wxRICHTEXT_NONE)
561 {
562 SetCachedSize(wxDefaultSize);
563 SetMaxSize(wxDefaultSize);
564 SetMinSize(wxDefaultSize);
565 }
5d7836c4
JS
566}
567
44219ff0 568// Convert units in tenths of a millimetre to device units
cdaed652 569int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
5d7836c4 570{
44219ff0 571 // Unscale
bec80f4f
JS
572 double scale = 1.0;
573 if (GetBuffer())
574 scale = GetBuffer()->GetScale();
575 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
576
44219ff0
JS
577 return p;
578}
579
580// Convert units in tenths of a millimetre to device units
bec80f4f 581int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
44219ff0 582{
5d7836c4
JS
583 // There are ppi pixels in 254.1 "1/10 mm"
584
585 double pixels = ((double) units * (double)ppi) / 254.1;
bec80f4f
JS
586 if (scale != 1.0)
587 pixels /= scale;
5d7836c4 588
603f702b
JS
589 // If the result is very small, make it at least one pixel in size.
590 if (pixels == 0 && units > 0)
591 pixels = 1;
592
5d7836c4
JS
593 return (int) pixels;
594}
595
24777478
JS
596// Convert units in pixels to tenths of a millimetre
597int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
598{
599 int p = pixels;
bec80f4f
JS
600 double scale = 1.0;
601 if (GetBuffer())
602 scale = GetBuffer()->GetScale();
603
604 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
24777478
JS
605}
606
bec80f4f 607int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
24777478
JS
608{
609 // There are ppi pixels in 254.1 "1/10 mm"
bec80f4f
JS
610
611 double p = double(pixels);
612
613 if (scale != 1.0)
614 p *= scale;
615
616 int units = int( p * 254.1 / (double) ppi );
24777478
JS
617 return units;
618}
619
bec80f4f 620// Draw the borders and background for the given rectangle and attributes.
603f702b
JS
621// Width and height are taken to be the outer margin size, not the content.
622bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
bec80f4f
JS
623{
624 // Assume boxRect is the area around the content
603f702b
JS
625 wxRect marginRect = boxRect;
626 wxRect contentRect, borderRect, paddingRect, outlineRect;
bec80f4f 627
603f702b 628 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
bec80f4f
JS
629
630 // Margin is transparent. Draw background from margin.
603f702b 631 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
bec80f4f 632 {
603f702b
JS
633 wxColour colour;
634 if (flags & wxRICHTEXT_DRAW_SELECTED)
635 {
636 // TODO: get selection colour from control?
637 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
638 }
639 else
640 colour = attr.GetBackgroundColour();
641
642 wxPen pen(colour);
643 wxBrush brush(colour);
bec80f4f
JS
644
645 dc.SetPen(pen);
646 dc.SetBrush(brush);
647 dc.DrawRectangle(marginRect);
648 }
649
603f702b
JS
650 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
651 {
652 wxRichTextAttr editBorderAttr = attr;
653 // TODO: make guideline colour configurable
654 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
655 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
656 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
657
658 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
659 }
660
661 if (attr.GetTextBoxAttr().GetBorder().IsValid())
662 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
bec80f4f 663
603f702b
JS
664 if (attr.GetTextBoxAttr().GetOutline().IsValid())
665 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
bec80f4f
JS
666
667 return true;
668}
669
670// Draw a border
603f702b 671bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
bec80f4f
JS
672{
673 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
603f702b 674 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 675
603f702b 676 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
677 {
678 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
679 wxColour col(attr.GetLeft().GetColour());
680
681 // If pen width is > 1, resorts to a solid rectangle.
682 if (borderLeft == 1)
683 {
684 int penStyle = wxSOLID;
685 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
686 penStyle = wxDOT;
687 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
688 penStyle = wxLONG_DASH;
603f702b 689 wxPen pen(col, 1, penStyle);
bec80f4f
JS
690 dc.SetPen(pen);
691 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
692
693 }
694 else if (borderLeft > 1)
695 {
696 wxPen pen(col);
697 wxBrush brush(col);
698 dc.SetPen(pen);
699 dc.SetBrush(brush);
700 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
701 }
702 }
703
603f702b 704 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
705 {
706 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
707
708 wxColour col(attr.GetRight().GetColour());
709
710 // If pen width is > 1, resorts to a solid rectangle.
711 if (borderRight == 1)
712 {
713 int penStyle = wxSOLID;
714 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
715 penStyle = wxDOT;
716 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
717 penStyle = wxLONG_DASH;
603f702b 718 wxPen pen(col, 1, penStyle);
bec80f4f 719 dc.SetPen(pen);
603f702b 720 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
bec80f4f
JS
721
722 }
723 else if (borderRight > 1)
724 {
725 wxPen pen(col);
726 wxBrush brush(col);
727 dc.SetPen(pen);
728 dc.SetBrush(brush);
729 dc.DrawRectangle(rect.x - borderRight, rect.y, borderRight, rect.height);
730 }
731 }
732
603f702b 733 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
734 {
735 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
736
737 wxColour col(attr.GetTop().GetColour());
738
739 // If pen width is > 1, resorts to a solid rectangle.
740 if (borderTop == 1)
741 {
742 int penStyle = wxSOLID;
743 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
744 penStyle = wxDOT;
745 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
746 penStyle = wxLONG_DASH;
603f702b 747 wxPen pen(col, 1, penStyle);
bec80f4f
JS
748 dc.SetPen(pen);
749 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
750
751 }
752 else if (borderTop > 1)
753 {
754 wxPen pen(col);
755 wxBrush brush(col);
756 dc.SetPen(pen);
757 dc.SetBrush(brush);
758 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
759 }
760 }
761
603f702b 762 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
763 {
764 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
765 wxColour col(attr.GetTop().GetColour());
766
767 // If pen width is > 1, resorts to a solid rectangle.
768 if (borderBottom == 1)
769 {
770 int penStyle = wxSOLID;
771 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
772 penStyle = wxDOT;
773 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
774 penStyle = wxLONG_DASH;
603f702b 775 wxPen pen(col, 1, penStyle);
bec80f4f
JS
776 dc.SetPen(pen);
777 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
778
779 }
780 else if (borderBottom > 1)
781 {
782 wxPen pen(col);
783 wxBrush brush(col);
784 dc.SetPen(pen);
785 dc.SetBrush(brush);
786 dc.DrawRectangle(rect.x, rect.y - rect.height - borderBottom, rect.width, borderBottom);
787 }
788 }
789
790 return true;
791}
792
793// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
794// or marginRect (outer), and the other must be the default rectangle (no width or height).
795// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
796// is available.
797//
798// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
799
603f702b 800bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
bec80f4f
JS
801{
802 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
803 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
804 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
805 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
806
603f702b 807 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 808
603f702b 809 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f 810 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
603f702b 811 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
bec80f4f 812 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
603f702b 813 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
bec80f4f 814 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
603f702b 815 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f
JS
816 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
817
603f702b 818 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f 819 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
603f702b 820 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
bec80f4f 821 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
603f702b 822 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
bec80f4f 823 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
603f702b 824 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f
JS
825 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
826
603f702b 827 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
bec80f4f 828 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
603f702b 829 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
bec80f4f 830 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
603f702b 831 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
bec80f4f 832 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b 833 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
bec80f4f
JS
834 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
835
603f702b 836 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
bec80f4f 837 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
603f702b 838 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
bec80f4f 839 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
603f702b 840 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
bec80f4f 841 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
603f702b 842 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
bec80f4f
JS
843 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
844
845 int leftTotal = marginLeft + borderLeft + paddingLeft;
846 int rightTotal = marginRight + borderRight + paddingRight;
847 int topTotal = marginTop + borderTop + paddingTop;
848 int bottomTotal = marginBottom + borderBottom + paddingBottom;
849
850 if (marginRect != wxRect())
851 {
852 contentRect.x = marginRect.x + leftTotal;
853 contentRect.y = marginRect.y + topTotal;
854 contentRect.width = marginRect.width - (leftTotal + rightTotal);
855 contentRect.height = marginRect.height - (topTotal + bottomTotal);
856 }
857 else
858 {
859 marginRect.x = contentRect.x - leftTotal;
860 marginRect.y = contentRect.y - topTotal;
861 marginRect.width = contentRect.width + (leftTotal + rightTotal);
862 marginRect.height = contentRect.height + (topTotal + bottomTotal);
863 }
864
865 borderRect.x = marginRect.x + marginLeft;
866 borderRect.y = marginRect.y + marginTop;
867 borderRect.width = marginRect.width - (marginLeft + marginRight);
868 borderRect.height = marginRect.height - (marginTop + marginBottom);
869
870 paddingRect.x = marginRect.x + marginLeft + borderLeft;
871 paddingRect.y = marginRect.y + marginTop + borderTop;
872 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
873 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
874
875 // The outline is outside the margin and doesn't influence the overall box position or content size.
876 outlineRect.x = marginRect.x - outlineLeft;
877 outlineRect.y = marginRect.y - outlineTop;
878 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
879 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
880
881 return true;
882}
883
603f702b
JS
884// Get the total margin for the object in pixels, taking into account margin, padding and border size
885bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
886 int& topMargin, int& bottomMargin)
887{
888 // Assume boxRect is the area around the content
889 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
890 marginRect = wxRect(0, 0, 1000, 1000);
bec80f4f 891
603f702b
JS
892 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
893
894 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
895 rightMargin = marginRect.GetRight() - contentRect.GetRight();
896 topMargin = contentRect.GetTop() - marginRect.GetTop();
897 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
898
899 return true;
900}
901
902// Returns the rectangle which the child has available to it given restrictions specified in the
903// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
904wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace)
905{
906 wxRect rect = availableParentSpace;
907 double scale = 1.0;
908 if (buffer)
909 scale = buffer->GetScale();
910
911 wxTextAttrDimensionConverter converter(dc, scale, availableParentSpace.GetSize());
912
913 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
914 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
915
916 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
917 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
918
919 // Can specify either left or right for the position (we're assuming we can't
920 // set the left and right edges to effectively set the size. Would we want to do that?)
921 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
922 {
923 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
924 }
925 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
926 {
927 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
928 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
929 rect.x = availableParentSpace.x + availableParentSpace.width - rect.width;
930 else
931 rect.x += x;
932 }
933
934 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
935 {
936 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
937 }
938 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
939 {
940 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
941 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
942 rect.y = availableParentSpace.y + availableParentSpace.height - rect.height;
943 else
944 rect.y += y;
945 }
946
947 return rect;
948}
949
950// Dump to output stream for debugging
5d7836c4
JS
951void wxRichTextObject::Dump(wxTextOutputStream& stream)
952{
953 stream << GetClassInfo()->GetClassName() << wxT("\n");
954 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");
955 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");
956}
957
603f702b 958// Gets the containing buffer
44219ff0
JS
959wxRichTextBuffer* wxRichTextObject::GetBuffer() const
960{
961 const wxRichTextObject* obj = this;
962 while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer)))
963 obj = obj->GetParent();
964 return wxDynamicCast(obj, wxRichTextBuffer);
965}
5d7836c4 966
603f702b
JS
967// Get the absolute object position, by traversing up the child/parent hierarchy
968wxPoint wxRichTextObject::GetAbsolutePosition() const
969{
970 wxPoint pt = GetPosition();
971
972 wxRichTextObject* p = GetParent();
973 while (p)
974 {
975 pt = pt + p->GetPosition();
976 p = p->GetParent();
977 }
978
979 return pt;
980}
981
982// Hit-testing: returns a flag indicating hit test details, plus
983// information about position
984int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
985{
986 if (!IsShown())
987 return wxRICHTEXT_HITTEST_NONE;
988
989 wxRect rect = GetRect();
990 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
991 pt.y >= rect.y && pt.y < rect.y + rect.height)
992 {
993 *obj = this;
994 *contextObj = GetParentContainer();
995 textPosition = GetRange().GetStart();
996 return wxRICHTEXT_HITTEST_ON;
997 }
998 else
999 return wxRICHTEXT_HITTEST_NONE;
1000}
1001
1002// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1003// lays out the object again using the maximum ('best') size
1004bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextBuffer* buffer,
1005 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr, const wxRect& availableParentSpace,
1006 int style)
1007{
1008 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace);
1009 wxRect originalAvailableRect = availableChildRect;
1010 Layout(dc, availableChildRect, style);
1011
1012 wxSize maxSize = GetMaxSize();
1013
1014 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1015 // on this basis
1016 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width /* && maxSize.x > 0 */)
1017 {
1018 // Redo the layout with a fixed, minimum size this time.
1019 Invalidate(wxRICHTEXT_ALL);
1020 wxRichTextAttr newAttr(attr);
1021 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1022 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1023
1024 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace);
1025
1026 // If a paragraph, align the whole paragraph.
1027 // Problem with this: if we're limited by a floating object, a line may be centered
1028 // w.r.t. the smaller resulting box rather than the actual available width.
1029 if (attr.HasAlignment())
1030 {
1031 // centering, right-justification
1032 if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
1033 {
1034 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1035 }
1036 else if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
1037 {
1038 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1039 }
1040 }
1041
1042 Layout(dc, availableChildRect, style);
1043 }
1044
1045 /*
1046 __________________
1047 | ____________ |
1048 | | | |
1049
1050
1051 */
1052
1053 return true;
1054}
1055
1056// Move the object recursively, by adding the offset from old to new
1057void wxRichTextObject::Move(const wxPoint& pt)
1058{
1059 SetPosition(pt);
1060}
1061
1062
5d7836c4
JS
1063/*!
1064 * wxRichTextCompositeObject
1065 * This is the base for drawable objects.
1066 */
1067
1068IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1069
1070wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1071 wxRichTextObject(parent)
1072{
1073}
1074
1075wxRichTextCompositeObject::~wxRichTextCompositeObject()
1076{
1077 DeleteChildren();
1078}
1079
1080/// Get the nth child
1081wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1082{
1083 wxASSERT ( n < m_children.GetCount() );
1084
1085 return m_children.Item(n)->GetData();
1086}
1087
1088/// Append a child, returning the position
1089size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1090{
1091 m_children.Append(child);
1092 child->SetParent(this);
1093 return m_children.GetCount() - 1;
1094}
1095
1096/// Insert the child in front of the given object, or at the beginning
1097bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1098{
1099 if (inFrontOf)
1100 {
1101 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1102 m_children.Insert(node, child);
1103 }
1104 else
1105 m_children.Insert(child);
1106 child->SetParent(this);
1107
1108 return true;
1109}
1110
1111/// Delete the child
1112bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1113{
1114 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1115 if (node)
1116 {
efbf6735
JS
1117 wxRichTextObject* obj = node->GetData();
1118 m_children.Erase(node);
5d7836c4 1119 if (deleteChild)
efbf6735 1120 delete obj;
5d7836c4
JS
1121
1122 return true;
1123 }
1124 return false;
1125}
1126
1127/// Delete all children
1128bool wxRichTextCompositeObject::DeleteChildren()
1129{
1130 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1131 while (node)
1132 {
1133 wxRichTextObjectList::compatibility_iterator oldNode = node;
1134
1135 wxRichTextObject* child = node->GetData();
1136 child->Dereference(); // Only delete if reference count is zero
1137
1138 node = node->GetNext();
efbf6735 1139 m_children.Erase(oldNode);
5d7836c4
JS
1140 }
1141
1142 return true;
1143}
1144
1145/// Get the child count
1146size_t wxRichTextCompositeObject::GetChildCount() const
1147{
1148 return m_children.GetCount();
1149}
1150
1151/// Copy
1152void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1153{
1154 wxRichTextObject::Copy(obj);
1155
1156 DeleteChildren();
1157
1158 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1159 while (node)
1160 {
1161 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
1162 wxRichTextObject* newChild = child->Clone();
1163 newChild->SetParent(this);
1164 m_children.Append(newChild);
5d7836c4
JS
1165
1166 node = node->GetNext();
1167 }
1168}
1169
1170/// Hit-testing: returns a flag indicating hit test details, plus
1171/// information about position
603f702b 1172int wxRichTextCompositeObject::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 1173{
603f702b
JS
1174 if (!IsShown())
1175 return wxRICHTEXT_HITTEST_NONE;
1176
5d7836c4
JS
1177 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1178 while (node)
1179 {
1180 wxRichTextObject* child = node->GetData();
1181
603f702b
JS
1182 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1183 {
1184 // Just check if we hit the overall object
1185 int ret = child->wxRichTextObject::HitTest(dc, pt, textPosition, obj, contextObj, flags);
1186 if (ret != wxRICHTEXT_HITTEST_NONE)
1187 return ret;
1188 }
1189 else if (child->IsShown())
1190 {
1191 int ret = child->HitTest(dc, pt, textPosition, obj, contextObj, flags);
1192 if (ret != wxRICHTEXT_HITTEST_NONE)
1193 return ret;
1194 }
5d7836c4
JS
1195
1196 node = node->GetNext();
1197 }
1198
603f702b 1199 return wxRICHTEXT_HITTEST_NONE;
5d7836c4
JS
1200}
1201
1202/// Finds the absolute position and row height for the given character position
1203bool wxRichTextCompositeObject::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
1204{
1205 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1206 while (node)
1207 {
1208 wxRichTextObject* child = node->GetData();
1209
603f702b
JS
1210 // Don't recurse if the child is a top-level object,
1211 // such as a text box, because the character position will no longer
1212 // apply. By definition, a top-level object has its own range of
1213 // character positions.
1214 if (!child->IsTopLevel() && child->FindPosition(dc, index, pt, height, forceLineStart))
5d7836c4
JS
1215 return true;
1216
1217 node = node->GetNext();
1218 }
1219
1220 return false;
1221}
1222
1223/// Calculate range
1224void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1225{
1226 long current = start;
1227 long lastEnd = current;
1228
603f702b
JS
1229 if (IsTopLevel())
1230 {
1231 current = 0;
1232 lastEnd = 0;
1233 }
1234
5d7836c4
JS
1235 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1236 while (node)
1237 {
1238 wxRichTextObject* child = node->GetData();
1239 long childEnd = 0;
1240
1241 child->CalculateRange(current, childEnd);
1242 lastEnd = childEnd;
1243
1244 current = childEnd + 1;
1245
1246 node = node->GetNext();
1247 }
1248
603f702b
JS
1249 if (IsTopLevel())
1250 {
1251 // A top-level object always has a range of size 1,
1252 // because its children don't count at this level.
1253 end = start;
1254 m_range.SetRange(start, start);
5d7836c4 1255
603f702b
JS
1256 // An object with no children has zero length
1257 if (m_children.GetCount() == 0)
1258 lastEnd --;
1259 m_ownRange.SetRange(0, lastEnd);
1260 }
1261 else
1262 {
1263 end = lastEnd;
5d7836c4 1264
603f702b
JS
1265 // An object with no children has zero length
1266 if (m_children.GetCount() == 0)
1267 end --;
1268
1269 m_range.SetRange(start, end);
1270 }
5d7836c4
JS
1271}
1272
1273/// Delete range from layout.
1274bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1275{
1276 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1277
5d7836c4
JS
1278 while (node)
1279 {
1280 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1281 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1282
5d7836c4
JS
1283 // Delete the range in each paragraph
1284
1285 // When a chunk has been deleted, internally the content does not
1286 // now match the ranges.
1287 // However, so long as deletion is not done on the same object twice this is OK.
1288 // If you may delete content from the same object twice, recalculate
1289 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1290 // adjust the range you're deleting accordingly.
7fe8059f 1291
5d7836c4
JS
1292 if (!obj->GetRange().IsOutside(range))
1293 {
603f702b
JS
1294 // No need to delete within a top-level object; just removing this object will do fine
1295 if (!obj->IsTopLevel())
1296 obj->DeleteRange(range);
5d7836c4
JS
1297
1298 // Delete an empty object, or paragraph within this range.
1299 if (obj->IsEmpty() ||
1300 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1301 {
1302 // An empty paragraph has length 1, so won't be deleted unless the
1303 // whole range is deleted.
7fe8059f 1304 RemoveChild(obj, true);
5d7836c4
JS
1305 }
1306 }
7fe8059f 1307
5d7836c4
JS
1308 node = next;
1309 }
7fe8059f 1310
5d7836c4
JS
1311 return true;
1312}
1313
1314/// Get any text in this object for the given range
1315wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1316{
1317 wxString text;
1318 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1319 while (node)
1320 {
1321 wxRichTextObject* child = node->GetData();
1322 wxRichTextRange childRange = range;
1323 if (!child->GetRange().IsOutside(range))
1324 {
1325 childRange.LimitTo(child->GetRange());
7fe8059f 1326
5d7836c4 1327 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1328
5d7836c4
JS
1329 text += childText;
1330 }
1331 node = node->GetNext();
1332 }
1333
1334 return text;
1335}
1336
603f702b
JS
1337/// Get the child object at the given character position
1338wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1339{
1340 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1341 while (node)
1342 {
1343 wxRichTextObject* child = node->GetData();
1344 if (child->GetRange().GetStart() == pos)
1345 return child;
1346 node = node->GetNext();
1347 }
1348 return NULL;
1349}
1350
5d7836c4 1351/// Recursively merge all pieces that can be merged.
109bfc88 1352bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
5d7836c4
JS
1353{
1354 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1355 while (node)
1356 {
1357 wxRichTextObject* child = node->GetData();
5cb0b827 1358 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
5d7836c4 1359 {
109bfc88
JS
1360 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1361 if (composite)
1362 composite->Defragment();
1363
1364 if (node->GetNext())
5d7836c4 1365 {
109bfc88
JS
1366 wxRichTextObject* nextChild = node->GetNext()->GetData();
1367 if (child->CanMerge(nextChild) && child->Merge(nextChild))
1368 {
1369 nextChild->Dereference();
1370 m_children.Erase(node->GetNext());
5d7836c4 1371
109bfc88
JS
1372 // Don't set node -- we'll see if we can merge again with the next
1373 // child.
1374 }
1375 else
1376 node = node->GetNext();
5d7836c4
JS
1377 }
1378 else
1379 node = node->GetNext();
1380 }
1381 else
1382 node = node->GetNext();
1383 }
1384
bec80f4f
JS
1385 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1386 if (GetChildCount() > 1)
5d7836c4 1387 {
bec80f4f
JS
1388 node = m_children.GetFirst();
1389 while (node)
1390 {
1391 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1392 wxRichTextObject* child = node->GetData();
1393 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1394 {
1395 if (child->IsEmpty())
1396 {
1397 child->Dereference();
1398 m_children.Erase(node);
1399 }
1400 node = next;
1401 }
1402 else
1403 node = node->GetNext();
1404 }
5d7836c4 1405 }
5d7836c4 1406
5d7836c4
JS
1407 return true;
1408}
1409
bec80f4f
JS
1410/// Dump to output stream for debugging
1411void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1412{
1413 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1414 while (node)
1415 {
1416 wxRichTextObject* child = node->GetData();
bec80f4f 1417 child->Dump(stream);
5d7836c4
JS
1418 node = node->GetNext();
1419 }
5d7836c4
JS
1420}
1421
603f702b
JS
1422/// Get/set the object size for the given range. Returns false if the range
1423/// is invalid for this object.
1424bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
1425{
1426 if (!range.IsWithin(GetRange()))
1427 return false;
5d7836c4 1428
603f702b 1429 wxSize sz;
5d7836c4 1430
603f702b
JS
1431 wxArrayInt childExtents;
1432 wxArrayInt* p;
1433 if (partialExtents)
1434 p = & childExtents;
1435 else
1436 p = NULL;
5d7836c4 1437
603f702b
JS
1438 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1439 while (node)
cdaed652 1440 {
603f702b
JS
1441 wxRichTextObject* child = node->GetData();
1442 if (!child->GetRange().IsOutside(range))
1443 {
1444 // Floating objects have a zero size within the paragraph.
1445 if (child->IsFloating())
1446 {
1447 if (partialExtents)
1448 {
1449 int lastSize;
1450 if (partialExtents->GetCount() > 0)
1451 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1452 else
1453 lastSize = 0;
cdaed652 1454
603f702b
JS
1455 partialExtents->Add(0 /* zero size */ + lastSize);
1456 }
1457 }
1458 else
1459 {
1460 wxSize childSize;
5d7836c4 1461
603f702b
JS
1462 wxRichTextRange rangeToUse = range;
1463 rangeToUse.LimitTo(child->GetRange());
1464 if (child->IsTopLevel())
1465 rangeToUse = child->GetOwnRange();
5d7836c4 1466
603f702b 1467 int childDescent = 0;
cdaed652 1468
603f702b
JS
1469 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1470 // but it's only going to be used after caching has taken place.
1471 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1472 {
1473 childDescent = child->GetDescent();
1474 childSize = child->GetCachedSize();
bec80f4f 1475
603f702b
JS
1476 sz.y = wxMax(sz.y, childSize.y);
1477 sz.x += childSize.x;
1478 descent = wxMax(descent, childDescent);
1479 }
1480 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p))
1481 {
1482 sz.y = wxMax(sz.y, childSize.y);
1483 sz.x += childSize.x;
1484 descent = wxMax(descent, childDescent);
bec80f4f 1485
603f702b
JS
1486 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1487 {
1488 child->SetCachedSize(childSize);
1489 child->SetDescent(childDescent);
1490 }
bec80f4f 1491
603f702b
JS
1492 if (partialExtents)
1493 {
1494 int lastSize;
1495 if (partialExtents->GetCount() > 0)
1496 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1497 else
1498 lastSize = 0;
bec80f4f 1499
603f702b
JS
1500 size_t i;
1501 for (i = 0; i < childExtents.GetCount(); i++)
1502 {
1503 partialExtents->Add(childExtents[i] + lastSize);
1504 }
1505 }
1506 }
1507 }
1508
1509 if (p)
1510 p->Clear();
1511 }
1512
1513 node = node->GetNext();
1514 }
1515 size = sz;
1516 return true;
1517}
1518
1519// Invalidate the buffer. With no argument, invalidates whole buffer.
1520void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1521{
1522 wxRichTextObject::Invalidate(invalidRange);
1523
1524 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1525 while (node)
1526 {
1527 wxRichTextObject* child = node->GetData();
1528 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1529 {
1530 // Skip
1531 }
1532 else if (child->IsTopLevel())
1533 {
1534 if (invalidRange == wxRICHTEXT_NONE)
1535 child->Invalidate(wxRICHTEXT_NONE);
1536 else
1537 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1538 }
1539 else
1540 child->Invalidate(invalidRange);
1541 node = node->GetNext();
1542 }
1543}
1544
1545// Move the object recursively, by adding the offset from old to new
1546void wxRichTextCompositeObject::Move(const wxPoint& pt)
1547{
1548 wxPoint oldPos = GetPosition();
1549 SetPosition(pt);
1550 wxPoint offset = pt - oldPos;
1551
1552 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1553 while (node)
1554 {
1555 wxRichTextObject* child = node->GetData();
1556 wxPoint childPos = child->GetPosition() + offset;
1557 child->Move(childPos);
1558 node = node->GetNext();
1559 }
1560}
1561
1562
1563/*!
1564 * wxRichTextParagraphLayoutBox
1565 * This box knows how to lay out paragraphs.
1566 */
1567
1568IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1569
1570wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1571 wxRichTextCompositeObject(parent)
1572{
1573 Init();
1574}
1575
1576wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1577{
1578 if (m_floatCollector)
1579 {
1580 delete m_floatCollector;
1581 m_floatCollector = NULL;
1582 }
1583}
1584
1585/// Initialize the object.
1586void wxRichTextParagraphLayoutBox::Init()
1587{
1588 m_ctrl = NULL;
1589
1590 // For now, assume is the only box and has no initial size.
1591 m_range = wxRichTextRange(0, -1);
1592 m_ownRange = wxRichTextRange(0, -1);
1593
1594 m_invalidRange = wxRICHTEXT_ALL;
1595
1596 SetMargins(4);
1597 m_partialParagraph = false;
1598 m_floatCollector = NULL;
1599}
1600
1601void wxRichTextParagraphLayoutBox::Clear()
1602{
1603 DeleteChildren();
1604
1605 if (m_floatCollector)
1606 delete m_floatCollector;
1607 m_floatCollector = NULL;
1608 m_partialParagraph = false;
1609}
1610
1611/// Copy
1612void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1613{
1614 Clear();
1615
1616 wxRichTextCompositeObject::Copy(obj);
1617
1618 m_partialParagraph = obj.m_partialParagraph;
1619 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1620}
1621
cdaed652 1622// Gather information about floating objects
603f702b 1623bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1624{
1625 if (m_floatCollector != NULL)
1626 delete m_floatCollector;
603f702b 1627 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652
VZ
1628 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1629 while (node && node->GetData() != untilObj)
1630 {
1631 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1632 wxASSERT (child != NULL);
1633 if (child)
1634 m_floatCollector->CollectFloat(child);
1635 node = node->GetNext();
1636 }
ce00f59b 1637
cdaed652
VZ
1638 return true;
1639}
1640
603f702b
JS
1641// Returns the style sheet associated with the overall buffer.
1642wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1643{
1644 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1645}
1646
1647// Get the number of floating objects at this level
1648int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1649{
1650 if (m_floatCollector)
1651 return m_floatCollector->GetFloatingObjectCount();
1652 else
1653 return 0;
1654}
1655
1656// Get a list of floating objects
1657bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1658{
1659 if (m_floatCollector)
1660 {
1661 return m_floatCollector->GetFloatingObjects(objects);
1662 }
1663 else
1664 return false;
1665}
1666
1667// Calculate ranges
1668void wxRichTextParagraphLayoutBox::UpdateRanges()
1669{
1670 long start = 0;
1671 if (GetParent())
1672 start = GetRange().GetStart();
1673 long end;
1674 CalculateRange(start, end);
1675}
1676
cdaed652 1677// HitTest
603f702b 1678int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1679{
603f702b
JS
1680 if (!IsShown())
1681 return wxRICHTEXT_HITTEST_NONE;
1682
cdaed652
VZ
1683 int ret = wxRICHTEXT_HITTEST_NONE;
1684 if (m_floatCollector)
603f702b 1685 ret = m_floatCollector->HitTest(dc, pt, textPosition, obj, flags);
ce00f59b 1686
cdaed652 1687 if (ret == wxRICHTEXT_HITTEST_NONE)
603f702b 1688 return wxRichTextCompositeObject::HitTest(dc, pt, textPosition, obj, contextObj, flags);
cdaed652 1689 else
603f702b
JS
1690 {
1691 *contextObj = this;
cdaed652 1692 return ret;
603f702b 1693 }
cdaed652
VZ
1694}
1695
1696/// Draw the floating objects
603f702b 1697void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
1698{
1699 if (m_floatCollector)
603f702b 1700 m_floatCollector->Draw(dc, range, selection, rect, descent, style);
cdaed652
VZ
1701}
1702
bec80f4f 1703void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1704{
1705 if (from == to)
1706 return;
1707
1708 from->RemoveChild(obj);
1709 to->AppendChild(obj);
5d7836c4
JS
1710}
1711
1712/// Draw the item
603f702b 1713bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1714{
603f702b
JS
1715 if (!IsShown())
1716 return true;
1717
1718 wxRect thisRect(GetPosition(), GetCachedSize());
1719
1720 int flags = style;
1721 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1722 flags |= wxRICHTEXT_DRAW_SELECTED;
1723
1724 // Don't draw guidelines if at top level
1725 int theseFlags = flags;
1726 if (!GetParent())
1727 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
1728 DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), thisRect, theseFlags);
1729
1730 DrawFloats(dc, range, selection, rect, descent, style);
5d7836c4
JS
1731 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1732 while (node)
1733 {
603f702b 1734 wxRichTextObject* child = node->GetData();
7fe8059f 1735
5d7836c4
JS
1736 if (child && !child->GetRange().IsOutside(range))
1737 {
1738 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1739 wxRichTextRange childRange = range;
1740 if (child->IsTopLevel())
1741 {
1742 childRange = child->GetOwnRange();
1743 }
7fe8059f 1744
ea160b2e
JS
1745 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1746 {
1747 // Stop drawing
1748 break;
1749 }
1750 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1751 {
1752 // Skip
1753 }
1754 else
603f702b 1755 child->Draw(dc, childRange, selection, rect, descent, style);
5d7836c4
JS
1756 }
1757
1758 node = node->GetNext();
1759 }
1760 return true;
1761}
1762
1763/// Lay the item out
38113684 1764bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int style)
5d7836c4 1765{
603f702b
JS
1766 SetPosition(rect.GetPosition());
1767
1768 if (!IsShown())
1769 return true;
1770
4d551ad5
JS
1771 wxRect availableSpace;
1772 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1773
1774 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1775 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1776 // so that during a size, only the visible part will be relaid out, or
1777 // it would take too long causing flicker. As an approximation, we assume that
1778 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1779 if (formatRect)
1780 {
603f702b
JS
1781 wxRect rect2(0, 0, rect.width, rect.height);
1782 availableSpace = GetAvailableContentArea(dc, rect2);
4d551ad5
JS
1783
1784 // Invalidate the part of the buffer from the first visible line
1785 // to the end. If other parts of the buffer are currently invalid,
1786 // then they too will be taken into account if they are above
1787 // the visible point.
1788 long startPos = 0;
1789 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1790 if (line)
1791 startPos = line->GetAbsoluteRange().GetStart();
1792
603f702b 1793 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1794 }
1795 else
603f702b
JS
1796 {
1797 availableSpace = GetAvailableContentArea(dc, rect);
1798 }
1799
1800 int leftMargin, rightMargin, topMargin, bottomMargin;
1801 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), GetAttributes(), leftMargin, rightMargin,
1802 topMargin, bottomMargin);
5d7836c4
JS
1803
1804 int maxWidth = 0;
603f702b
JS
1805 int maxHeight = 0;
1806
1807 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1808 int maxMaxWidth = 0;
1809
1810 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1811 int maxMinWidth = 0;
1812
1813 // If we have vertical alignment, we must recalculate everything.
1814 bool hasVerticalAlignment = (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1815 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1816
5d7836c4 1817 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1818
38113684 1819 bool layoutAll = true;
1e967276 1820
38113684
JS
1821 // Get invalid range, rounding to paragraph start/end.
1822 wxRichTextRange invalidRange = GetInvalidRange(true);
1823
4d551ad5 1824 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1825 return true;
1826
603f702b 1827 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1828 layoutAll = true;
38113684 1829 else // If we know what range is affected, start laying out from that point on.
603f702b 1830 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1831 {
38113684 1832 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1833 if (firstParagraph)
1834 {
1835 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1836 wxRichTextObjectList::compatibility_iterator previousNode;
1837 if ( firstNode )
1838 previousNode = firstNode->GetPrevious();
9b4af7b7 1839 if (firstNode)
2c375f42 1840 {
9b4af7b7
JS
1841 if (previousNode)
1842 {
1843 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1844 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1845 }
7fe8059f 1846
2c375f42
JS
1847 // Now we're going to start iterating from the first affected paragraph.
1848 node = firstNode;
1e967276
JS
1849
1850 layoutAll = false;
2c375f42
JS
1851 }
1852 }
1853 }
1854
603f702b 1855 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1856
4d551ad5
JS
1857 // A way to force speedy rest-of-buffer layout (the 'else' below)
1858 bool forceQuickLayout = false;
39a1c2f2 1859
5d7836c4
JS
1860 while (node)
1861 {
1862 // Assume this box only contains paragraphs
1863
1864 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
9a83f860 1865 wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 1866
603f702b 1867 if (child && child->IsShown())
2c375f42 1868 {
603f702b
JS
1869 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1870 if ( !forceQuickLayout &&
1871 (layoutAll ||
1872 child->GetLines().IsEmpty() ||
1873 !child->GetRange().IsOutside(invalidRange)) )
1874 {
1875 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1876 // lays out the object again using the minimum size
1877 child->LayoutToBestSize(dc, GetBuffer(),
1878 GetAttributes(), child->GetAttributes(), availableSpace, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
1879
1880 // Layout must set the cached size
1881 availableSpace.y += child->GetCachedSize().y;
1882 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1883 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1884 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1885
1886 // If we're just formatting the visible part of the buffer,
1887 // and we're now past the bottom of the window, and we don't have any
1888 // floating objects (since they may cause wrapping to change for the rest of the
1889 // the buffer), start quick layout.
1890 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
1891 forceQuickLayout = true;
1892 }
1893 else
1894 {
1895 // We're outside the immediately affected range, so now let's just
1896 // move everything up or down. This assumes that all the children have previously
1897 // been laid out and have wrapped line lists associated with them.
1898 // TODO: check all paragraphs before the affected range.
1899
1900 int inc = availableSpace.y - child->GetPosition().y;
1901
1902 while (node)
1903 {
1904 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1905 if (child)
1906 {
1907 if (child->GetLines().GetCount() == 0)
1908 {
1909 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1910 // lays out the object again using the minimum size
1911 child->LayoutToBestSize(dc, GetBuffer(),
1912 GetAttributes(), child->GetAttributes(), availableSpace, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
1913
1914 //child->Layout(dc, availableChildRect, style);
1915 }
1916 else
1917 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 1918
603f702b
JS
1919 availableSpace.y += child->GetCachedSize().y;
1920 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1921 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1922 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1923 }
4d551ad5 1924
603f702b
JS
1925 node = node->GetNext();
1926 }
1927 break;
1928 }
2c375f42 1929 }
7fe8059f 1930
603f702b
JS
1931 node = node->GetNext();
1932 }
1933
1934 node = m_children.GetLast();
1935 if (node && node->GetData()->IsShown())
1936 {
1937 wxRichTextObject* child = node->GetData();
1938 // maxHeight = (child->GetPosition().y - GetPosition().y) + child->GetCachedSize().y;
1939 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
1940 }
1941 else
1942 maxHeight = 0; // topMargin + bottomMargin;
1943
1944 // TODO: (also in para layout) should set the
1945 // object's size to an absolute one if specified,
1946 // but if not specified, calculate it from content.
1947
1948 // We need to add back the margins etc.
1949 {
1950 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
1951 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
1952 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
1953 SetCachedSize(marginRect.GetSize());
1954 }
1955
1956 // The maximum size is the greatest of all maximum widths for all paragraphs.
1957 {
1958 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
1959 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
1960 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
1961 SetMaxSize(marginRect.GetSize());
1962 }
1963
1964 // The minimum size is the greatest of all minimum widths for all paragraphs.
1965 {
1966 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
1967 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
1968 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
1969 SetMinSize(marginRect.GetSize());
1970 }
1971
1972 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1973 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
1974 {
1975 int yOffset = 0;
1976 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
1977 if (leftOverSpace > 0)
1978 {
1979 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
1980 {
1981 yOffset = (leftOverSpace/2);
1982 }
1983 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
1984 {
1985 yOffset = leftOverSpace;
1986 }
1987 }
7fe8059f 1988
603f702b
JS
1989 // Move all the children to vertically align the content
1990 // This doesn't take into account floating objects, unfortunately.
1991 if (yOffset != 0)
1992 {
1993 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
1994 while (node)
1995 {
1996 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1997 if (child)
603f702b 1998 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
1999
2000 node = node->GetNext();
2c375f42 2001 }
2c375f42 2002 }
5d7836c4
JS
2003 }
2004
1e967276 2005 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2006
2007 return true;
2008}
2009
5d7836c4 2010/// Get/set the size for the given range.
31778480 2011bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
5d7836c4
JS
2012{
2013 wxSize sz;
2014
09f14108
JS
2015 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2016 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2017
2018 // First find the first paragraph whose starting position is within the range.
2019 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2020 while (node)
2021 {
2022 // child is a paragraph
2023 wxRichTextObject* child = node->GetData();
2024 const wxRichTextRange& r = child->GetRange();
2025
2026 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2027 {
2028 startPara = node;
2029 break;
2030 }
2031
2032 node = node->GetNext();
2033 }
2034
2035 // Next find the last paragraph containing part of the range
2036 node = m_children.GetFirst();
2037 while (node)
2038 {
2039 // child is a paragraph
2040 wxRichTextObject* child = node->GetData();
2041 const wxRichTextRange& r = child->GetRange();
2042
2043 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2044 {
2045 endPara = node;
2046 break;
2047 }
2048
2049 node = node->GetNext();
2050 }
2051
2052 if (!startPara || !endPara)
2053 return false;
2054
2055 // Now we can add up the sizes
2056 for (node = startPara; node ; node = node->GetNext())
2057 {
2058 // child is a paragraph
2059 wxRichTextObject* child = node->GetData();
2060 const wxRichTextRange& childRange = child->GetRange();
2061 wxRichTextRange rangeToFind = range;
2062 rangeToFind.LimitTo(childRange);
2063
603f702b
JS
2064 if (child->IsTopLevel())
2065 rangeToFind = child->GetOwnRange();
2066
5d7836c4
JS
2067 wxSize childSize;
2068
2069 int childDescent = 0;
7f0d9d71 2070 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, flags, position);
5d7836c4
JS
2071
2072 descent = wxMax(childDescent, descent);
2073
2074 sz.x = wxMax(sz.x, childSize.x);
2075 sz.y += childSize.y;
2076
2077 if (node == endPara)
2078 break;
2079 }
2080
2081 size = sz;
2082
2083 return true;
2084}
2085
2086/// Get the paragraph at the given position
2087wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2088{
2089 if (caretPosition)
2090 pos ++;
2091
2092 // First find the first paragraph whose starting position is within the range.
2093 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2094 while (node)
2095 {
2096 // child is a paragraph
2097 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2098 // wxASSERT (child != NULL);
5d7836c4 2099
603f702b
JS
2100 if (child)
2101 {
2102 // Return first child in buffer if position is -1
2103 // if (pos == -1)
2104 // return child;
5d7836c4 2105
603f702b
JS
2106 if (child->GetRange().Contains(pos))
2107 return child;
2108 }
5d7836c4
JS
2109
2110 node = node->GetNext();
2111 }
2112 return NULL;
2113}
2114
2115/// Get the line at the given position
2116wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2117{
2118 if (caretPosition)
2119 pos ++;
2120
2121 // First find the first paragraph whose starting position is within the range.
2122 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2123 while (node)
2124 {
7051fa41
JS
2125 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2126 if (obj->GetRange().Contains(pos))
5d7836c4 2127 {
7051fa41
JS
2128 // child is a paragraph
2129 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2130 // wxASSERT (child != NULL);
7051fa41 2131
603f702b 2132 if (child)
7051fa41 2133 {
603f702b
JS
2134 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2135 while (node2)
2136 {
2137 wxRichTextLine* line = node2->GetData();
5d7836c4 2138
603f702b 2139 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2140
603f702b 2141 if (range.Contains(pos) ||
5d7836c4 2142
603f702b
JS
2143 // If the position is end-of-paragraph, then return the last line of
2144 // of the paragraph.
2145 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2146 return line;
5d7836c4 2147
603f702b
JS
2148 node2 = node2->GetNext();
2149 }
7051fa41 2150 }
7fe8059f 2151 }
5d7836c4
JS
2152
2153 node = node->GetNext();
2154 }
2155
2156 int lineCount = GetLineCount();
2157 if (lineCount > 0)
2158 return GetLineForVisibleLineNumber(lineCount-1);
2159 else
2160 return NULL;
2161}
2162
2163/// Get the line at the given y pixel position, or the last line.
2164wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2165{
2166 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2167 while (node)
2168 {
2169 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2170 // wxASSERT (child != NULL);
5d7836c4 2171
603f702b 2172 if (child)
5d7836c4 2173 {
603f702b
JS
2174 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2175 while (node2)
2176 {
2177 wxRichTextLine* line = node2->GetData();
5d7836c4 2178
603f702b 2179 wxRect rect(line->GetRect());
5d7836c4 2180
603f702b
JS
2181 if (y <= rect.GetBottom())
2182 return line;
5d7836c4 2183
603f702b
JS
2184 node2 = node2->GetNext();
2185 }
7fe8059f 2186 }
5d7836c4
JS
2187
2188 node = node->GetNext();
2189 }
2190
2191 // Return last line
2192 int lineCount = GetLineCount();
2193 if (lineCount > 0)
2194 return GetLineForVisibleLineNumber(lineCount-1);
2195 else
2196 return NULL;
2197}
2198
2199/// Get the number of visible lines
2200int wxRichTextParagraphLayoutBox::GetLineCount() const
2201{
2202 int count = 0;
2203
2204 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2205 while (node)
2206 {
2207 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2208 // wxASSERT (child != NULL);
2209
2210 if (child)
2211 count += child->GetLines().GetCount();
5d7836c4 2212
5d7836c4
JS
2213 node = node->GetNext();
2214 }
2215 return count;
2216}
2217
2218
2219/// Get the paragraph for a given line
2220wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2221{
1e967276 2222 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2223}
2224
2225/// Get the line size at the given position
2226wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2227{
2228 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2229 if (line)
2230 {
2231 return line->GetSize();
2232 }
2233 else
2234 return wxSize(0, 0);
2235}
2236
2237
2238/// Convenience function to add a paragraph of text
24777478 2239wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2240{
fe5aa22c 2241 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2242 // be combined at display time.
2243 // Divide into paragraph and character styles.
3e541562 2244
24777478
JS
2245 wxRichTextAttr defaultCharStyle;
2246 wxRichTextAttr defaultParaStyle;
4f32b3cf 2247
5607c890
JS
2248 // If the default style is a named paragraph style, don't apply any character formatting
2249 // to the initial text string.
2250 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2251 {
2252 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2253 if (def)
2254 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2255 }
2256 else
2257 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2258
24777478
JS
2259 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2260 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2261
2262 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
5d7836c4
JS
2263
2264 AppendChild(para);
2265
2266 UpdateRanges();
5d7836c4
JS
2267
2268 return para->GetRange();
2269}
2270
2271/// Adds multiple paragraphs, based on newlines.
24777478 2272wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2273{
fe5aa22c 2274 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2275 // be combined at display time.
2276 // Divide into paragraph and character styles.
3e541562 2277
24777478
JS
2278 wxRichTextAttr defaultCharStyle;
2279 wxRichTextAttr defaultParaStyle;
5607c890
JS
2280
2281 // If the default style is a named paragraph style, don't apply any character formatting
2282 // to the initial text string.
2283 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2284 {
2285 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2286 if (def)
2287 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2288 }
2289 else
2290 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2291
24777478
JS
2292 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2293 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2294
5d7836c4
JS
2295 wxRichTextParagraph* firstPara = NULL;
2296 wxRichTextParagraph* lastPara = NULL;
2297
2298 wxRichTextRange range(-1, -1);
0ca07313 2299
5d7836c4 2300 size_t i = 0;
28f92d74 2301 size_t len = text.length();
5d7836c4 2302 wxString line;
4f32b3cf 2303 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
0ca07313
JS
2304
2305 AppendChild(para);
2306
2307 firstPara = para;
2308 lastPara = para;
2309
5d7836c4
JS
2310 while (i < len)
2311 {
2312 wxChar ch = text[i];
2313 if (ch == wxT('\n') || ch == wxT('\r'))
2314 {
99404ab0
JS
2315 if (i != (len-1))
2316 {
2317 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2318 plainText->SetText(line);
0ca07313 2319
99404ab0 2320 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
5d7836c4 2321
99404ab0 2322 AppendChild(para);
0ca07313 2323
99404ab0
JS
2324 lastPara = para;
2325 line = wxEmptyString;
2326 }
5d7836c4
JS
2327 }
2328 else
2329 line += ch;
2330
2331 i ++;
2332 }
0ca07313 2333
7fe8059f 2334 if (!line.empty())
5d7836c4 2335 {
0ca07313
JS
2336 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2337 plainText->SetText(line);
5d7836c4
JS
2338 }
2339
5d7836c4 2340 UpdateRanges();
0ca07313 2341
0ca07313 2342 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2343}
2344
2345/// Convenience function to add an image
24777478 2346wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2347{
fe5aa22c 2348 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2349 // be combined at display time.
2350 // Divide into paragraph and character styles.
3e541562 2351
24777478
JS
2352 wxRichTextAttr defaultCharStyle;
2353 wxRichTextAttr defaultParaStyle;
5607c890
JS
2354
2355 // If the default style is a named paragraph style, don't apply any character formatting
2356 // to the initial text string.
2357 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2358 {
2359 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2360 if (def)
2361 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2362 }
2363 else
2364 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2365
24777478
JS
2366 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2367 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2368
4f32b3cf
JS
2369 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2370 AppendChild(para);
2371 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2372
5d7836c4 2373 UpdateRanges();
5d7836c4
JS
2374
2375 return para->GetRange();
2376}
2377
2378
2379/// Insert fragment into this box at the given position. If partialParagraph is true,
2380/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2381/// marker.
5d7836c4 2382
0ca07313 2383bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2384{
5d7836c4
JS
2385 // First, find the first paragraph whose starting position is within the range.
2386 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2387 if (para)
2388 {
24777478 2389 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2390
5d7836c4
JS
2391 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2392
2393 // Now split at this position, returning the object to insert the new
2394 // ones in front of.
2395 wxRichTextObject* nextObject = para->SplitAt(position);
2396
2397 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2398 // text, for example, so let's optimize.
2399
2400 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2401 {
2402 // Add the first para to this para...
2403 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2404 if (!firstParaNode)
2405 return false;
2406
2407 // Iterate through the fragment paragraph inserting the content into this paragraph.
2408 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2409 wxASSERT (firstPara != NULL);
2410
2411 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2412 while (objectNode)
2413 {
2414 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2415
5d7836c4
JS
2416 if (!nextObject)
2417 {
2418 // Append
2419 para->AppendChild(newObj);
2420 }
2421 else
2422 {
2423 // Insert before nextObject
2424 para->InsertChild(newObj, nextObject);
2425 }
7fe8059f 2426
5d7836c4
JS
2427 objectNode = objectNode->GetNext();
2428 }
2429
2430 return true;
2431 }
2432 else
2433 {
2434 // Procedure for inserting a fragment consisting of a number of
2435 // paragraphs:
2436 //
2437 // 1. Remove and save the content that's after the insertion point, for adding
2438 // back once we've added the fragment.
2439 // 2. Add the content from the first fragment paragraph to the current
2440 // paragraph.
2441 // 3. Add remaining fragment paragraphs after the current paragraph.
2442 // 4. Add back the saved content from the first paragraph. If partialParagraph
2443 // is true, add it to the last paragraph added and not a new one.
2444
2445 // 1. Remove and save objects after split point.
2446 wxList savedObjects;
2447 if (nextObject)
2448 para->MoveToList(nextObject, savedObjects);
2449
2450 // 2. Add the content from the 1st fragment paragraph.
2451 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2452 if (!firstParaNode)
2453 return false;
2454
2455 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2456 wxASSERT(firstPara != NULL);
2457
6c0ea513
JS
2458 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2459 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2460
2461 // Save empty paragraph attributes for appending later
2462 // These are character attributes deliberately set for a new paragraph. Without this,
2463 // we couldn't pass default attributes when appending a new paragraph.
24777478 2464 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2465
5d7836c4 2466 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2467
2468 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2469 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2470
5d7836c4
JS
2471 while (objectNode)
2472 {
c025e094 2473 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2474
c025e094
JS
2475 // Append
2476 para->AppendChild(newObj);
7fe8059f 2477
5d7836c4
JS
2478 objectNode = objectNode->GetNext();
2479 }
2480
2481 // 3. Add remaining fragment paragraphs after the current paragraph.
2482 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2483 wxRichTextObject* nextParagraph = NULL;
2484 if (nextParagraphNode)
2485 nextParagraph = nextParagraphNode->GetData();
2486
2487 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2488 wxRichTextParagraph* finalPara = para;
2489
99404ab0
JS
2490 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2491
5d7836c4 2492 // If there was only one paragraph, we need to insert a new one.
99404ab0 2493 while (i)
5d7836c4 2494 {
99404ab0
JS
2495 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2496 wxASSERT( para != NULL );
5d7836c4 2497
99404ab0 2498 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2499
2500 if (nextParagraph)
2501 InsertChild(finalPara, nextParagraph);
2502 else
7fe8059f 2503 AppendChild(finalPara);
99404ab0
JS
2504
2505 i = i->GetNext();
5d7836c4 2506 }
5d7836c4 2507
99404ab0
JS
2508 // If there was only one paragraph, or we have full paragraphs in our fragment,
2509 // we need to insert a new one.
2510 if (needExtraPara)
2511 {
2512 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2513
2514 if (nextParagraph)
2515 InsertChild(finalPara, nextParagraph);
2516 else
2517 AppendChild(finalPara);
5d7836c4
JS
2518 }
2519
2520 // 4. Add back the remaining content.
2521 if (finalPara)
2522 {
c025e094
JS
2523 if (nextObject)
2524 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2525
2526 // Ensure there's at least one object
2527 if (finalPara->GetChildCount() == 0)
2528 {
7fe8059f 2529 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2530 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2531
2532 finalPara->AppendChild(text);
2533 }
2534 }
2535
6c0ea513
JS
2536 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2537 finalPara->SetAttributes(firstPara->GetAttributes());
2538 else if (finalPara && finalPara != para)
99404ab0
JS
2539 finalPara->SetAttributes(originalAttr);
2540
5d7836c4
JS
2541 return true;
2542 }
2543 }
2544 else
2545 {
2546 // Append
2547 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2548 while (i)
2549 {
2550 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2551 wxASSERT( para != NULL );
7fe8059f 2552
5d7836c4 2553 AppendChild(para->Clone());
7fe8059f 2554
5d7836c4
JS
2555 i = i->GetNext();
2556 }
2557
2558 return true;
2559 }
5d7836c4
JS
2560}
2561
2562/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2563/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2564bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2565{
2566 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2567 while (i)
2568 {
2569 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2570 wxASSERT( para != NULL );
2571
2572 if (!para->GetRange().IsOutside(range))
2573 {
2574 fragment.AppendChild(para->Clone());
7fe8059f 2575 }
5d7836c4
JS
2576 i = i->GetNext();
2577 }
2578
2579 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2580 if (!fragment.IsEmpty())
2581 {
2582 wxRichTextRange topTailRange(range);
2583
2584 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2585 wxASSERT( firstPara != NULL );
2586
2587 // Chop off the start of the paragraph
2588 if (topTailRange.GetStart() > firstPara->GetRange().GetStart())
2589 {
2590 wxRichTextRange r(firstPara->GetRange().GetStart(), topTailRange.GetStart()-1);
2591 firstPara->DeleteRange(r);
2592
2593 // Make sure the numbering is correct
2594 long end;
2595 fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
2596
2597 // Now, we've deleted some positions, so adjust the range
2598 // accordingly.
2599 topTailRange.SetEnd(topTailRange.GetEnd() - r.GetLength());
2600 }
2601
2602 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2603 wxASSERT( lastPara != NULL );
2604
2605 if (topTailRange.GetEnd() < (lastPara->GetRange().GetEnd()-1))
2606 {
2607 wxRichTextRange r(topTailRange.GetEnd()+1, lastPara->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */
2608 lastPara->DeleteRange(r);
2609
2610 // Make sure the numbering is correct
2611 long end;
2612 fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
2613
2614 // We only have part of a paragraph at the end
2615 fragment.SetPartialParagraph(true);
2616 }
2617 else
2618 {
2619 if (topTailRange.GetEnd() == (lastPara->GetRange().GetEnd() - 1))
2620 // We have a partial paragraph (don't save last new paragraph marker)
2621 fragment.SetPartialParagraph(true);
2622 else
2623 // We have a complete paragraph
2624 fragment.SetPartialParagraph(false);
2625 }
2626 }
2627
2628 return true;
2629}
2630
2631/// Given a position, get the number of the visible line (potentially many to a paragraph),
2632/// starting from zero at the start of the buffer.
2633long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2634{
2635 if (caretPosition)
2636 pos ++;
2637
2638 int lineCount = 0;
2639
2640 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2641 while (node)
2642 {
2643 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2644 // wxASSERT( child != NULL );
5d7836c4 2645
603f702b 2646 if (child)
5d7836c4 2647 {
603f702b 2648 if (child->GetRange().Contains(pos))
5d7836c4 2649 {
603f702b
JS
2650 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2651 while (node2)
5d7836c4 2652 {
603f702b
JS
2653 wxRichTextLine* line = node2->GetData();
2654 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2655
603f702b
JS
2656 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2657 {
2658 // If the caret is displayed at the end of the previous wrapped line,
2659 // we want to return the line it's _displayed_ at (not the actual line
2660 // containing the position).
2661 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2662 return lineCount - 1;
2663 else
2664 return lineCount;
2665 }
7fe8059f 2666
603f702b
JS
2667 lineCount ++;
2668
2669 node2 = node2->GetNext();
2670 }
2671 // If we didn't find it in the lines, it must be
2672 // the last position of the paragraph. So return the last line.
2673 return lineCount-1;
5d7836c4 2674 }
603f702b
JS
2675 else
2676 lineCount += child->GetLines().GetCount();
5d7836c4 2677 }
5d7836c4
JS
2678
2679 node = node->GetNext();
2680 }
2681
2682 // Not found
2683 return -1;
2684}
2685
2686/// Given a line number, get the corresponding wxRichTextLine object.
2687wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2688{
2689 int lineCount = 0;
2690
2691 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2692 while (node)
2693 {
2694 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2695 // wxASSERT(child != NULL);
5d7836c4 2696
603f702b 2697 if (child)
5d7836c4 2698 {
603f702b 2699 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2700 {
603f702b
JS
2701 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2702 while (node2)
2703 {
2704 wxRichTextLine* line = node2->GetData();
7fe8059f 2705
603f702b
JS
2706 if (lineCount == lineNumber)
2707 return line;
5d7836c4 2708
603f702b 2709 lineCount ++;
7fe8059f 2710
603f702b
JS
2711 node2 = node2->GetNext();
2712 }
7fe8059f 2713 }
603f702b
JS
2714 else
2715 lineCount += child->GetLines().GetCount();
5d7836c4 2716 }
5d7836c4
JS
2717
2718 node = node->GetNext();
2719 }
2720
2721 // Didn't find it
2722 return NULL;
2723}
2724
2725/// Delete range from layout.
2726bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2727{
2728 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2729
99404ab0 2730 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2731 while (node)
2732 {
2733 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2734 // wxASSERT (obj != NULL);
5d7836c4
JS
2735
2736 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2737
603f702b 2738 if (obj)
5d7836c4 2739 {
603f702b 2740 // Delete the range in each paragraph
99404ab0 2741
603f702b 2742 if (!obj->GetRange().IsOutside(range))
5d7836c4 2743 {
603f702b
JS
2744 // Deletes the content of this object within the given range
2745 obj->DeleteRange(range);
99404ab0 2746
603f702b
JS
2747 wxRichTextRange thisRange = obj->GetRange();
2748 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2749
603f702b
JS
2750 // If the whole paragraph is within the range to delete,
2751 // delete the whole thing.
2752 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2753 {
603f702b
JS
2754 // Delete the whole object
2755 RemoveChild(obj, true);
2756 obj = NULL;
99404ab0 2757 }
603f702b
JS
2758 else if (!firstPara)
2759 firstPara = obj;
5d7836c4 2760
603f702b
JS
2761 // If the range includes the paragraph end, we need to join this
2762 // and the next paragraph.
2763 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2764 {
603f702b
JS
2765 // We need to move the objects from the next paragraph
2766 // to this paragraph
2767
2768 wxRichTextParagraph* nextParagraph = NULL;
2769 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2770 nextParagraph = obj;
6c0ea513 2771 else
603f702b
JS
2772 {
2773 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2774 if (next)
2775 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2776 }
5d7836c4 2777
603f702b
JS
2778 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2779
2780 wxRichTextAttr nextParaAttr;
2781 if (applyFinalParagraphStyle)
2782 {
2783 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2784 // not the next one.
2785 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2786 nextParaAttr = thisAttr;
2787 else
2788 nextParaAttr = nextParagraph->GetAttributes();
2789 }
5d7836c4 2790
603f702b 2791 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2792 {
603f702b
JS
2793 // Move the objects to the previous para
2794 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2795
603f702b
JS
2796 while (node1)
2797 {
2798 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2799
603f702b 2800 firstPara->AppendChild(obj1);
5d7836c4 2801
603f702b
JS
2802 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2803 nextParagraph->GetChildren().Erase(node1);
99404ab0 2804
603f702b
JS
2805 node1 = next1;
2806 }
5d7836c4 2807
603f702b
JS
2808 // Delete the paragraph
2809 RemoveChild(nextParagraph, true);
2810 }
fa01bfdd 2811
603f702b
JS
2812 // Avoid empty paragraphs
2813 if (firstPara && firstPara->GetChildren().GetCount() == 0)
2814 {
2815 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2816 firstPara->AppendChild(text);
2817 }
99404ab0 2818
603f702b
JS
2819 if (applyFinalParagraphStyle)
2820 firstPara->SetAttributes(nextParaAttr);
2821
2822 return true;
2823 }
5d7836c4
JS
2824 }
2825 }
7fe8059f 2826
5d7836c4
JS
2827 node = next;
2828 }
7fe8059f 2829
5d7836c4
JS
2830 return true;
2831}
2832
2833/// Get any text in this object for the given range
2834wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
2835{
2836 int lineCount = 0;
2837 wxString text;
2838 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2839 while (node)
2840 {
2841 wxRichTextObject* child = node->GetData();
2842 if (!child->GetRange().IsOutside(range))
2843 {
5d7836c4
JS
2844 wxRichTextRange childRange = range;
2845 childRange.LimitTo(child->GetRange());
7fe8059f 2846
5d7836c4 2847 wxString childText = child->GetTextForRange(childRange);
7fe8059f 2848
5d7836c4
JS
2849 text += childText;
2850
1a75935d 2851 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
2852 text += wxT("\n");
2853
5d7836c4
JS
2854 lineCount ++;
2855 }
2856 node = node->GetNext();
2857 }
2858
2859 return text;
2860}
2861
2862/// Get all the text
2863wxString wxRichTextParagraphLayoutBox::GetText() const
2864{
c99f1b0f 2865 return GetTextForRange(GetOwnRange());
5d7836c4
JS
2866}
2867
2868/// Get the paragraph by number
2869wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
2870{
27e20452 2871 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
2872 return NULL;
2873
2874 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
2875}
2876
2877/// Get the length of the paragraph
2878int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
2879{
2880 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
2881 if (para)
2882 return para->GetRange().GetLength() - 1; // don't include newline
2883 else
2884 return 0;
2885}
2886
2887/// Get the text of the paragraph
2888wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
2889{
2890 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
2891 if (para)
2892 return para->GetTextForRange(para->GetRange());
2893 else
2894 return wxEmptyString;
2895}
2896
2897/// Convert zero-based line column and paragraph number to a position.
2898long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
2899{
2900 wxRichTextParagraph* para = GetParagraphAtLine(y);
2901 if (para)
2902 {
2903 return para->GetRange().GetStart() + x;
2904 }
2905 else
2906 return -1;
2907}
2908
2909/// Convert zero-based position to line column and paragraph number
2910bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
2911{
2912 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
2913 if (para)
2914 {
2915 int count = 0;
2916 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2917 while (node)
2918 {
2919 wxRichTextObject* child = node->GetData();
2920 if (child == para)
2921 break;
2922 count ++;
2923 node = node->GetNext();
2924 }
2925
2926 *y = count;
2927 *x = pos - para->GetRange().GetStart();
2928
2929 return true;
2930 }
2931 else
2932 return false;
2933}
2934
2935/// Get the leaf object in a paragraph at this position.
2936/// Given a line number, get the corresponding wxRichTextLine object.
2937wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
2938{
2939 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2940 if (para)
2941 {
2942 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 2943
5d7836c4
JS
2944 while (node)
2945 {
2946 wxRichTextObject* child = node->GetData();
2947 if (child->GetRange().Contains(position))
2948 return child;
7fe8059f 2949
5d7836c4
JS
2950 node = node->GetNext();
2951 }
2952 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
2953 return para->GetChildren().GetLast()->GetData();
2954 }
2955 return NULL;
2956}
2957
2958/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 2959bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
2960{
2961 bool characterStyle = false;
2962 bool paragraphStyle = false;
2963
2964 if (style.IsCharacterStyle())
2965 characterStyle = true;
2966 if (style.IsParagraphStyle())
2967 paragraphStyle = true;
2968
603f702b
JS
2969 wxRichTextBuffer* buffer = GetBuffer();
2970
59509217
JS
2971 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
2972 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
2973 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
2974 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 2975 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 2976 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
2977
2978 // Apply paragraph style first, if any
24777478 2979 wxRichTextAttr wholeStyle(style);
523d2f14 2980
603f702b 2981 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 2982 {
603f702b 2983 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 2984 if (def)
603f702b 2985 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 2986 }
59509217
JS
2987
2988 // Limit the attributes to be set to the content to only character attributes.
24777478 2989 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
2990 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
2991
603f702b 2992 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 2993 {
603f702b 2994 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 2995 if (def)
603f702b 2996 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
2997 }
2998
5d7836c4
JS
2999 // If we are associated with a control, make undoable; otherwise, apply immediately
3000 // to the data.
3001
603f702b 3002 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3003
3004 wxRichTextAction* action = NULL;
7fe8059f 3005
5d7836c4
JS
3006 if (haveControl && withUndo)
3007 {
603f702b 3008 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3009 action->SetRange(range);
603f702b 3010 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3011 }
3012
3013 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3014 while (node)
3015 {
3016 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3017 // wxASSERT (para != NULL);
5d7836c4
JS
3018
3019 if (para && para->GetChildCount() > 0)
3020 {
3021 // Stop searching if we're beyond the range of interest
3022 if (para->GetRange().GetStart() > range.GetEnd())
3023 break;
3024
3025 if (!para->GetRange().IsOutside(range))
3026 {
3027 // We'll be using a copy of the paragraph to make style changes,
3028 // not updating the buffer directly.
4e09ebe8 3029 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3030
5d7836c4
JS
3031 if (haveControl && withUndo)
3032 {
3033 newPara = new wxRichTextParagraph(*para);
3034 action->GetNewParagraphs().AppendChild(newPara);
3035
3036 // Also store the old ones for Undo
3037 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3038 }
3039 else
3040 newPara = para;
41a85215 3041
a7ed48a5
JS
3042 // If we're specifying paragraphs only, then we really mean character formatting
3043 // to be included in the paragraph style
3044 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3045 {
aeb6ebe2
JS
3046 if (removeStyle)
3047 {
3048 // Removes the given style from the paragraph
3049 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3050 }
3051 else if (resetExistingStyle)
523d2f14
JS
3052 newPara->GetAttributes() = wholeStyle;
3053 else
59509217 3054 {
523d2f14
JS
3055 if (applyMinimal)
3056 {
3057 // Only apply attributes that will make a difference to the combined
3058 // style as seen on the display
603f702b 3059 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3060 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3061 }
3062 else
3063 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3064 }
59509217 3065 }
5d7836c4 3066
5912d19e 3067 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3068 // since they will computed as needed. Only apply the character styling if it's _only_
3069 // character styling. This policy is subject to change and might be put under user control.
3070
59509217
JS
3071 // Hm. we might well be applying a mix of paragraph and character styles, in which
3072 // case we _do_ want to apply character styles regardless of what para styles are set.
3073 // But if we're applying a paragraph style, which has some character attributes, but
3074 // we only want the paragraphs to hold this character style, then we _don't_ want to
3075 // apply the character style. So we need to be able to choose.
3076
f1d800d9 3077 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3078 {
3079 wxRichTextRange childRange(range);
3080 childRange.LimitTo(newPara->GetRange());
7fe8059f 3081
5d7836c4
JS
3082 // Find the starting position and if necessary split it so
3083 // we can start applying a different style.
3084 // TODO: check that the style actually changes or is different
3085 // from style outside of range
4e09ebe8
JS
3086 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3087 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3088
5d7836c4
JS
3089 if (childRange.GetStart() == newPara->GetRange().GetStart())
3090 firstObject = newPara->GetChildren().GetFirst()->GetData();
3091 else
3092 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3093
5d7836c4
JS
3094 // Increment by 1 because we're apply the style one _after_ the split point
3095 long splitPoint = childRange.GetEnd();
3096 if (splitPoint != newPara->GetRange().GetEnd())
3097 splitPoint ++;
7fe8059f 3098
5d7836c4 3099 // Find last object
4b3483e7 3100 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3101 lastObject = newPara->GetChildren().GetLast()->GetData();
3102 else
3103 // lastObject is set as a side-effect of splitting. It's
3104 // returned as the object before the new object.
3105 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3106
5d7836c4
JS
3107 wxASSERT(firstObject != NULL);
3108 wxASSERT(lastObject != NULL);
7fe8059f 3109
5d7836c4
JS
3110 if (!firstObject || !lastObject)
3111 continue;
7fe8059f 3112
5d7836c4
JS
3113 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3114 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3115
4c9847e1
MW
3116 wxASSERT(firstNode);
3117 wxASSERT(lastNode);
7fe8059f 3118
5d7836c4 3119 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3120
5d7836c4
JS
3121 while (node2)
3122 {
3123 wxRichTextObject* child = node2->GetData();
7fe8059f 3124
aeb6ebe2
JS
3125 if (removeStyle)
3126 {
3127 // Removes the given style from the paragraph
3128 wxRichTextRemoveStyle(child->GetAttributes(), style);
3129 }
3130 else if (resetExistingStyle)
523d2f14
JS
3131 child->GetAttributes() = characterAttributes;
3132 else
59509217 3133 {
523d2f14
JS
3134 if (applyMinimal)
3135 {
3136 // Only apply attributes that will make a difference to the combined
3137 // style as seen on the display
603f702b 3138 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3139 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3140 }
3141 else
3142 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3143 }
59509217 3144
5d7836c4
JS
3145 if (node2 == lastNode)
3146 break;
7fe8059f 3147
5d7836c4
JS
3148 node2 = node2->GetNext();
3149 }
3150 }
3151 }
3152 }
3153
3154 node = node->GetNext();
3155 }
3156
3157 // Do action, or delay it until end of batch.
3158 if (haveControl && withUndo)
603f702b 3159 buffer->SubmitAction(action);
5d7836c4
JS
3160
3161 return true;
3162}
3163
603f702b
JS
3164// Just change the attributes for this single object.
3165void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3166{
603f702b 3167 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3168 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3169 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3170 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3171
cdaed652 3172 wxRichTextAction *action = NULL;
603f702b
JS
3173 wxRichTextAttr newAttr = obj->GetAttributes();
3174 if (resetExistingStyle)
3175 newAttr = textAttr;
3176 else
3177 newAttr.Apply(textAttr);
cdaed652
VZ
3178
3179 if (haveControl && withUndo)
3180 {
603f702b
JS
3181 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3182 action->SetRange(obj->GetRange().FromInternal());
3183 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3184 action->MakeObject(obj);
bec80f4f 3185
603f702b 3186 action->GetAttributes() = newAttr;
cdaed652
VZ
3187 }
3188 else
603f702b 3189 obj->GetAttributes() = newAttr;
cdaed652
VZ
3190
3191 if (haveControl && withUndo)
603f702b 3192 buffer->SubmitAction(action);
cdaed652
VZ
3193}
3194
5d7836c4 3195/// Get the text attributes for this position.
24777478 3196bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3197{
fe5aa22c
JS
3198 return DoGetStyle(position, style, true);
3199}
e191ee87 3200
24777478 3201bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3202{
3203 return DoGetStyle(position, style, false);
3204}
3205
fe5aa22c
JS
3206/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3207/// context attributes.
24777478 3208bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3209{
4e09ebe8 3210 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3211
5d7836c4 3212 if (style.IsParagraphStyle())
fe5aa22c 3213 {
5d7836c4 3214 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3215 if (obj)
3216 {
fe5aa22c
JS
3217 if (combineStyles)
3218 {
3219 // Start with the base style
3220 style = GetAttributes();
e191ee87 3221
fe5aa22c
JS
3222 // Apply the paragraph style
3223 wxRichTextApplyStyle(style, obj->GetAttributes());
3224 }
3225 else
3226 style = obj->GetAttributes();
5912d19e 3227
fe5aa22c
JS
3228 return true;
3229 }
5d7836c4
JS
3230 }
3231 else
fe5aa22c
JS
3232 {
3233 obj = GetLeafObjectAtPosition(position);
3234 if (obj)
3235 {
fe5aa22c
JS
3236 if (combineStyles)
3237 {
3238 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3239 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3240 }
3241 else
3242 style = obj->GetAttributes();
5912d19e 3243
fe5aa22c
JS
3244 return true;
3245 }
fe5aa22c
JS
3246 }
3247 return false;
5d7836c4
JS
3248}
3249
59509217
JS
3250static bool wxHasStyle(long flags, long style)
3251{
3252 return (flags & style) != 0;
3253}
3254
3255/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3256/// content.
24777478
JS
3257bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3258{
3259 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3260
3261 return true;
3262}
3263
3264/// Get the combined style for a range - if any attribute is different within the range,
3265/// that attribute is not present within the flags.
3266/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3267/// nested.
3268bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3269{
24777478
JS
3270 style = wxRichTextAttr();
3271
3272 wxRichTextAttr clashingAttr;
3273 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3274
24777478
JS
3275 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3276 while (node)
59509217 3277 {
603f702b
JS
3278 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3279 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3280 {
24777478 3281 if (para->GetChildren().GetCount() == 0)
59509217 3282 {
603f702b 3283 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3284
24777478 3285 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
59509217
JS
3286 }
3287 else
3288 {
24777478
JS
3289 wxRichTextRange paraRange(para->GetRange());
3290 paraRange.LimitTo(range);
59509217 3291
24777478
JS
3292 // First collect paragraph attributes only
3293 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3294 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3295 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
9c4cb611 3296
24777478
JS
3297 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3298
3299 while (childNode)
59509217 3300 {
24777478
JS
3301 wxRichTextObject* child = childNode->GetData();
3302 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3303 {
603f702b 3304 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3305
24777478
JS
3306 // Now collect character attributes only
3307 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3308
24777478
JS
3309 CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
3310 }
59509217 3311
24777478 3312 childNode = childNode->GetNext();
59509217
JS
3313 }
3314 }
59509217 3315 }
24777478 3316 node = node->GetNext();
59509217 3317 }
24777478
JS
3318 return true;
3319}
59509217 3320
24777478
JS
3321/// Set default style
3322bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3323{
3324 m_defaultAttributes = style;
3325 return true;
3326}
59509217 3327
24777478
JS
3328/// Test if this whole range has character attributes of the specified kind. If any
3329/// of the attributes are different within the range, the test fails. You
3330/// can use this to implement, for example, bold button updating. style must have
3331/// flags indicating which attributes are of interest.
3332bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3333{
3334 int foundCount = 0;
3335 int matchingCount = 0;
59509217 3336
24777478
JS
3337 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3338 while (node)
59509217 3339 {
24777478 3340 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3341 // wxASSERT (para != NULL);
59509217 3342
24777478 3343 if (para)
59509217 3344 {
24777478
JS
3345 // Stop searching if we're beyond the range of interest
3346 if (para->GetRange().GetStart() > range.GetEnd())
3347 return foundCount == matchingCount && foundCount != 0;
59509217 3348
24777478 3349 if (!para->GetRange().IsOutside(range))
59509217 3350 {
24777478 3351 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3352
24777478
JS
3353 while (node2)
3354 {
3355 wxRichTextObject* child = node2->GetData();
3356 // Allow for empty string if no buffer
3357 wxRichTextRange childRange = child->GetRange();
3358 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3359 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3360
24777478
JS
3361 if (!childRange.IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
3362 {
3363 foundCount ++;
3364 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3365
24777478
JS
3366 if (wxTextAttrEqPartial(textAttr, style))
3367 matchingCount ++;
3368 }
59509217 3369
24777478
JS
3370 node2 = node2->GetNext();
3371 }
59509217
JS
3372 }
3373 }
59509217 3374
24777478 3375 node = node->GetNext();
59509217
JS
3376 }
3377
24777478
JS
3378 return foundCount == matchingCount && foundCount != 0;
3379}
59509217 3380
24777478
JS
3381/// Test if this whole range has paragraph attributes of the specified kind. If any
3382/// of the attributes are different within the range, the test fails. You
3383/// can use this to implement, for example, centering button updating. style must have
3384/// flags indicating which attributes are of interest.
3385bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3386{
3387 int foundCount = 0;
3388 int matchingCount = 0;
3389
3390 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3391 while (node)
38f833b1 3392 {
24777478 3393 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3394 // wxASSERT (para != NULL);
24777478
JS
3395
3396 if (para)
38f833b1 3397 {
24777478
JS
3398 // Stop searching if we're beyond the range of interest
3399 if (para->GetRange().GetStart() > range.GetEnd())
3400 return foundCount == matchingCount && foundCount != 0;
3401
3402 if (!para->GetRange().IsOutside(range))
38f833b1 3403 {
24777478
JS
3404 wxRichTextAttr textAttr = GetAttributes();
3405 // Apply the paragraph style
3406 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3407
3408 foundCount ++;
3409 if (wxTextAttrEqPartial(textAttr, style))
3410 matchingCount ++;
38f833b1
JS
3411 }
3412 }
24777478
JS
3413
3414 node = node->GetNext();
38f833b1 3415 }
24777478
JS
3416 return foundCount == matchingCount && foundCount != 0;
3417}
5d7836c4 3418
5d7836c4
JS
3419void wxRichTextParagraphLayoutBox::Reset()
3420{
3421 Clear();
3422
603f702b
JS
3423 wxRichTextBuffer* buffer = GetBuffer();
3424 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3425 {
603f702b
JS
3426 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3427 event.SetEventObject(buffer->GetRichTextCtrl());
3428 event.SetContainer(this);
cd8ba0d9
JS
3429
3430 buffer->SendEvent(event, true);
3431 }
3432
7fe8059f 3433 AddParagraph(wxEmptyString);
3e541562 3434
603f702b 3435 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3436}
3437
38113684
JS
3438/// Invalidate the buffer. With no argument, invalidates whole buffer.
3439void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3440{
603f702b 3441 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3442
603f702b
JS
3443 DoInvalidate(invalidRange);
3444}
3445
3446// Do the (in)validation for this object only
3447void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3448{
1e967276 3449 if (invalidRange == wxRICHTEXT_ALL)
38113684 3450 {
1e967276 3451 m_invalidRange = wxRICHTEXT_ALL;
38113684 3452 }
1e967276 3453 // Already invalidating everything
603f702b
JS
3454 else if (m_invalidRange == wxRICHTEXT_ALL)
3455 {
3456 }
3457 else
3458 {
3459 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3460 m_invalidRange.SetStart(invalidRange.GetStart());
3461 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3462 m_invalidRange.SetEnd(invalidRange.GetEnd());
3463 }
3464}
39a1c2f2 3465
603f702b
JS
3466// Do the (in)validation both up and down the hierarchy
3467void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3468{
3469 Invalidate(invalidRange);
3470
3471 if (invalidRange != wxRICHTEXT_NONE)
3472 {
3473 // Now go up the hierarchy
3474 wxRichTextObject* thisObj = this;
3475 wxRichTextObject* p = GetParent();
3476 while (p)
3477 {
3478 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3479 if (l)
3480 l->DoInvalidate(thisObj->GetRange());
3481
3482 thisObj = p;
3483 p = p->GetParent();
3484 }
3485 }
38113684
JS
3486}
3487
3488/// Get invalid range, rounding to entire paragraphs if argument is true.
3489wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3490{
1e967276 3491 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3492 return m_invalidRange;
39a1c2f2 3493
38113684 3494 wxRichTextRange range = m_invalidRange;
39a1c2f2 3495
38113684
JS
3496 if (wholeParagraphs)
3497 {
3498 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3499 if (para1)
3500 range.SetStart(para1->GetRange().GetStart());
cdaed652 3501 // floating layout make all child should be relayout
603f702b 3502 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3503 }
3504 return range;
3505}
3506
fe5aa22c
JS
3507/// Apply the style sheet to the buffer, for example if the styles have changed.
3508bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3509{
3510 wxASSERT(styleSheet != NULL);
3511 if (!styleSheet)
3512 return false;
3513
3514 int foundCount = 0;
3515
44580804
JS
3516 wxRichTextAttr attr(GetBasicStyle());
3517 if (GetBasicStyle().HasParagraphStyleName())
3518 {
3519 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3520 if (paraDef)
3521 {
3522 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3523 SetBasicStyle(attr);
3524 foundCount ++;
3525 }
3526 }
3527
3528 if (GetBasicStyle().HasCharacterStyleName())
3529 {
3530 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3531 if (charDef)
3532 {
3533 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3534 SetBasicStyle(attr);
3535 foundCount ++;
3536 }
3537 }
3538
fe5aa22c
JS
3539 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3540 while (node)
3541 {
3542 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3543 // wxASSERT (para != NULL);
fe5aa22c
JS
3544
3545 if (para)
3546 {
38f833b1
JS
3547 // Combine paragraph and list styles. If there is a list style in the original attributes,
3548 // the current indentation overrides anything else and is used to find the item indentation.
3549 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3550 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3551 // exception as above).
3552 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3553 // So when changing a list style interactively, could retrieve level based on current style, then
3554 // set appropriate indent and apply new style.
41a85215 3555
bbd55ff9
JS
3556 int outline = -1;
3557 int num = -1;
3558 if (para->GetAttributes().HasOutlineLevel())
3559 outline = para->GetAttributes().GetOutlineLevel();
3560 if (para->GetAttributes().HasBulletNumber())
3561 num = para->GetAttributes().GetBulletNumber();
3562
38f833b1
JS
3563 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3564 {
3565 int currentIndent = para->GetAttributes().GetLeftIndent();
3566
3567 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3568 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3569 if (paraDef && !listDef)
3570 {
336d8ae9 3571 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3572 foundCount ++;
3573 }
3574 else if (listDef && !paraDef)
3575 {
3576 // Set overall style defined for the list style definition
336d8ae9 3577 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3578
3579 // Apply the style for this level
3580 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3581 foundCount ++;
3582 }
3583 else if (listDef && paraDef)
3584 {
3585 // Combines overall list style, style for level, and paragraph style
336d8ae9 3586 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3587 foundCount ++;
3588 }
3589 }
3590 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3591 {
3592 int currentIndent = para->GetAttributes().GetLeftIndent();
3593
3594 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3595
41a85215 3596 // Overall list definition style
336d8ae9 3597 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3598
38f833b1
JS
3599 // Style for this level
3600 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3601
3602 foundCount ++;
3603 }
3604 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3605 {
3606 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3607 if (def)
3608 {
336d8ae9 3609 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3610 foundCount ++;
3611 }
3612 }
bbd55ff9
JS
3613
3614 if (outline != -1)
3615 para->GetAttributes().SetOutlineLevel(outline);
3616 if (num != -1)
3617 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3618 }
3619
3620 node = node->GetNext();
3621 }
3622 return foundCount != 0;
3623}
3624
38f833b1
JS
3625/// Set list style
3626bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3627{
603f702b
JS
3628 wxRichTextBuffer* buffer = GetBuffer();
3629 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 3630
38f833b1
JS
3631 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3632 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3633 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3634 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 3635
38f833b1
JS
3636 // Current number, if numbering
3637 int n = startFrom;
41a85215 3638
38f833b1
JS
3639 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3640
3641 // If we are associated with a control, make undoable; otherwise, apply immediately
3642 // to the data.
3643
603f702b 3644 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
3645
3646 wxRichTextAction* action = NULL;
3647
3648 if (haveControl && withUndo)
3649 {
603f702b 3650 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 3651 action->SetRange(range);
603f702b 3652 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
3653 }
3654
3655 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3656 while (node)
3657 {
3658 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3659 // wxASSERT (para != NULL);
38f833b1
JS
3660
3661 if (para && para->GetChildCount() > 0)
3662 {
3663 // Stop searching if we're beyond the range of interest
3664 if (para->GetRange().GetStart() > range.GetEnd())
3665 break;
3666
3667 if (!para->GetRange().IsOutside(range))
3668 {
3669 // We'll be using a copy of the paragraph to make style changes,
3670 // not updating the buffer directly.
3671 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3672
3673 if (haveControl && withUndo)
3674 {
3675 newPara = new wxRichTextParagraph(*para);
3676 action->GetNewParagraphs().AppendChild(newPara);
3677
3678 // Also store the old ones for Undo
3679 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3680 }
3681 else
3682 newPara = para;
41a85215 3683
38f833b1
JS
3684 if (def)
3685 {
3686 int thisIndent = newPara->GetAttributes().GetLeftIndent();
3687 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 3688
38f833b1
JS
3689 // How is numbering going to work?
3690 // If we are renumbering, or numbering for the first time, we need to keep
3691 // track of the number for each level. But we might be simply applying a different
3692 // list style.
3693 // In Word, applying a style to several paragraphs, even if at different levels,
3694 // reverts the level back to the same one. So we could do the same here.
3695 // Renumbering will need to be done when we promote/demote a paragraph.
3696
3697 // Apply the overall list style, and item style for this level
24777478 3698 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 3699 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 3700
d2d0adc7 3701 // Now we need to do numbering
38f833b1
JS
3702 if (renumber)
3703 {
3704 newPara->GetAttributes().SetBulletNumber(n);
3705 }
41a85215 3706
38f833b1
JS
3707 n ++;
3708 }
3709 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
3710 {
3711 // if def is NULL, remove list style, applying any associated paragraph style
3712 // to restore the attributes
3713
3714 newPara->GetAttributes().SetListStyleName(wxEmptyString);
3715 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 3716 newPara->GetAttributes().SetBulletText(wxEmptyString);
41a85215 3717
38f833b1 3718 // Eliminate the main list-related attributes
d2d0adc7 3719 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 3720
38f833b1
JS
3721 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
3722 {
3723 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
3724 if (def)
3725 {
336d8ae9 3726 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3727 }
3728 }
3729 }
3730 }
3731 }
3732
3733 node = node->GetNext();
3734 }
3735
3736 // Do action, or delay it until end of batch.
3737 if (haveControl && withUndo)
603f702b 3738 buffer->SubmitAction(action);
38f833b1
JS
3739
3740 return true;
3741}
3742
3743bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
3744{
603f702b
JS
3745 wxRichTextBuffer* buffer = GetBuffer();
3746 if (buffer && buffer->GetStyleSheet())
38f833b1 3747 {
603f702b 3748 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
3749 if (def)
3750 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
3751 }
3752 return false;
3753}
3754
3755/// Clear list for given range
3756bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
3757{
3758 return SetListStyle(range, NULL, flags);
3759}
3760
3761/// Number/renumber any list elements in the given range
3762bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3763{
3764 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
3765}
3766
3767/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
3768bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
3769 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3770{
603f702b
JS
3771 wxRichTextBuffer* buffer = GetBuffer();
3772 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 3773
38f833b1
JS
3774 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3775 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 3776#if wxDEBUG_LEVEL
38f833b1 3777 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 3778#endif
38f833b1
JS
3779
3780 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 3781
38f833b1
JS
3782 // Max number of levels
3783 const int maxLevels = 10;
41a85215 3784
38f833b1
JS
3785 // The level we're looking at now
3786 int currentLevel = -1;
41a85215 3787
38f833b1
JS
3788 // The item number for each level
3789 int levels[maxLevels];
3790 int i;
41a85215 3791
38f833b1
JS
3792 // Reset all numbering
3793 for (i = 0; i < maxLevels; i++)
3794 {
3795 if (startFrom != -1)
d2d0adc7 3796 levels[i] = startFrom-1;
38f833b1 3797 else if (renumber) // start again
d2d0adc7 3798 levels[i] = 0;
38f833b1
JS
3799 else
3800 levels[i] = -1; // start from the number we found, if any
3801 }
41a85215 3802
38f833b1
JS
3803 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3804
3805 // If we are associated with a control, make undoable; otherwise, apply immediately
3806 // to the data.
3807
603f702b 3808 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
3809
3810 wxRichTextAction* action = NULL;
3811
3812 if (haveControl && withUndo)
3813 {
603f702b 3814 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 3815 action->SetRange(range);
603f702b 3816 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
3817 }
3818
3819 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3820 while (node)
3821 {
3822 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3823 // wxASSERT (para != NULL);
38f833b1
JS
3824
3825 if (para && para->GetChildCount() > 0)
3826 {
3827 // Stop searching if we're beyond the range of interest
3828 if (para->GetRange().GetStart() > range.GetEnd())
3829 break;
3830
3831 if (!para->GetRange().IsOutside(range))
3832 {
3833 // We'll be using a copy of the paragraph to make style changes,
3834 // not updating the buffer directly.
3835 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3836
3837 if (haveControl && withUndo)
3838 {
3839 newPara = new wxRichTextParagraph(*para);
3840 action->GetNewParagraphs().AppendChild(newPara);
3841
3842 // Also store the old ones for Undo
3843 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3844 }
3845 else
3846 newPara = para;
41a85215 3847
38f833b1
JS
3848 wxRichTextListStyleDefinition* defToUse = def;
3849 if (!defToUse)
3850 {
336d8ae9
VZ
3851 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
3852 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 3853 }
41a85215 3854
38f833b1
JS
3855 if (defToUse)
3856 {
3857 int thisIndent = newPara->GetAttributes().GetLeftIndent();
3858 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
3859
d2d0adc7
JS
3860 // If we've specified a level to apply to all, change the level.
3861 if (specifiedLevel != -1)
38f833b1 3862 thisLevel = specifiedLevel;
41a85215 3863
38f833b1
JS
3864 // Do promotion if specified
3865 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
3866 {
3867 thisLevel = thisLevel - promoteBy;
3868 if (thisLevel < 0)
3869 thisLevel = 0;
3870 if (thisLevel > 9)
3871 thisLevel = 9;
3872 }
41a85215 3873
38f833b1 3874 // Apply the overall list style, and item style for this level
24777478 3875 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 3876 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 3877
38f833b1 3878 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 3879
38f833b1
JS
3880 if (currentLevel == -1)
3881 currentLevel = thisLevel;
41a85215 3882
38f833b1
JS
3883 // Same level as before, do nothing except increment level's number afterwards
3884 if (currentLevel == thisLevel)
3885 {
3886 }
3887 // A deeper level: start renumbering all levels after current level
3888 else if (thisLevel > currentLevel)
3889 {
3890 for (i = currentLevel+1; i <= thisLevel; i++)
3891 {
d2d0adc7 3892 levels[i] = 0;
38f833b1
JS
3893 }
3894 currentLevel = thisLevel;
3895 }
3896 else if (thisLevel < currentLevel)
3897 {
3898 currentLevel = thisLevel;
41a85215 3899 }
38f833b1
JS
3900
3901 // Use the current numbering if -1 and we have a bullet number already
3902 if (levels[currentLevel] == -1)
3903 {
3904 if (newPara->GetAttributes().HasBulletNumber())
3905 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
3906 else
3907 levels[currentLevel] = 1;
3908 }
d2d0adc7
JS
3909 else
3910 {
3911 levels[currentLevel] ++;
3912 }
41a85215 3913
38f833b1
JS
3914 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
3915
d2d0adc7
JS
3916 // Create the bullet text if an outline list
3917 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
3918 {
3919 wxString text;
3920 for (i = 0; i <= currentLevel; i++)
3921 {
3922 if (!text.IsEmpty())
3923 text += wxT(".");
3924 text += wxString::Format(wxT("%d"), levels[i]);
3925 }
3926 newPara->GetAttributes().SetBulletText(text);
3927 }
38f833b1
JS
3928 }
3929 }
3930 }
3931
3932 node = node->GetNext();
3933 }
3934
3935 // Do action, or delay it until end of batch.
3936 if (haveControl && withUndo)
603f702b 3937 buffer->SubmitAction(action);
38f833b1
JS
3938
3939 return true;
3940}
3941
3942bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
3943{
603f702b
JS
3944 wxRichTextBuffer* buffer = GetBuffer();
3945 if (buffer->GetStyleSheet())
38f833b1
JS
3946 {
3947 wxRichTextListStyleDefinition* def = NULL;
3948 if (!defName.IsEmpty())
603f702b 3949 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
3950 return NumberList(range, def, flags, startFrom, specifiedLevel);
3951 }
3952 return false;
3953}
3954
3955/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
3956bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
3957{
3958 // TODO
3959 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
3960 // to NumberList with a flag indicating promotion is required within one of the ranges.
3961 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
3962 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
3963 // We start renumbering from the para after that different para we found. We specify that the numbering of that
3964 // list position will start from 1.
3965 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
3966 // We can end the renumbering at this point.
41a85215 3967
38f833b1 3968 // For now, only renumber within the promotion range.
41a85215 3969
38f833b1
JS
3970 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
3971}
3972
3973bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
3974{
603f702b
JS
3975 wxRichTextBuffer* buffer = GetBuffer();
3976 if (buffer->GetStyleSheet())
38f833b1
JS
3977 {
3978 wxRichTextListStyleDefinition* def = NULL;
3979 if (!defName.IsEmpty())
603f702b 3980 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
3981 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
3982 }
3983 return false;
3984}
3985
d2d0adc7
JS
3986/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
3987/// position of the paragraph that it had to start looking from.
24777478 3988bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 3989{
d2d0adc7
JS
3990 if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
3991 return false;
3e541562 3992
603f702b
JS
3993 wxRichTextBuffer* buffer = GetBuffer();
3994 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 3995 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 3996 {
336d8ae9 3997 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
3998 if (def)
3999 {
4000 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4001 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4002
d2d0adc7
JS
4003 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4004
4005 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4006 if (previousParagraph->GetAttributes().HasBulletName())
4007 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4008 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4009 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4010
d2d0adc7
JS
4011 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4012 attr.SetBulletNumber(nextNumber);
3e541562 4013
d2d0adc7
JS
4014 if (isOutline)
4015 {
4016 wxString text = previousParagraph->GetAttributes().GetBulletText();
4017 if (!text.IsEmpty())
4018 {
4019 int pos = text.Find(wxT('.'), true);
4020 if (pos != wxNOT_FOUND)
4021 {
4022 text = text.Mid(0, text.Length() - pos - 1);
4023 }
4024 else
4025 text = wxEmptyString;
4026 if (!text.IsEmpty())
4027 text += wxT(".");
4028 text += wxString::Format(wxT("%d"), nextNumber);
4029 attr.SetBulletText(text);
4030 }
4031 }
3e541562 4032
d2d0adc7
JS
4033 return true;
4034 }
4035 else
4036 return false;
4037 }
4038 else
4039 return false;
4040}
4041
5d7836c4
JS
4042/*!
4043 * wxRichTextParagraph
4044 * This object represents a single paragraph (or in a straight text editor, a line).
4045 */
4046
603f702b 4047IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4048
cfa3b256
JS
4049wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4050
24777478 4051wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4052 wxRichTextCompositeObject(parent)
5d7836c4 4053{
5d7836c4
JS
4054 if (style)
4055 SetAttributes(*style);
4056}
4057
24777478 4058wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4059 wxRichTextCompositeObject(parent)
5d7836c4 4060{
4f32b3cf
JS
4061 if (paraStyle)
4062 SetAttributes(*paraStyle);
5d7836c4 4063
4f32b3cf 4064 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4065}
4066
4067wxRichTextParagraph::~wxRichTextParagraph()
4068{
4069 ClearLines();
4070}
4071
4072/// Draw the item
603f702b 4073bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4074{
603f702b
JS
4075 if (!IsShown())
4076 return true;
4077
4078 // Currently we don't merge these attributes with the parent, but we
4079 // should consider whether we should (e.g. if we set a border colour
4080 // for all paragraphs). But generally box attributes are likely to be
4081 // different for different objects.
4082 wxRect paraRect = GetRect();
4083 DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), paraRect);
4084
24777478 4085 wxRichTextAttr attr = GetCombinedAttributes();
fe5aa22c 4086
5d7836c4 4087 // Draw the bullet, if any
fe5aa22c 4088 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4 4089 {
fe5aa22c 4090 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4091 {
fe5aa22c 4092 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4093 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4094
24777478 4095 wxRichTextAttr bulletAttr(GetCombinedAttributes());
d2d0adc7 4096
e3eac0ff
JS
4097 // Combine with the font of the first piece of content, if one is specified
4098 if (GetChildren().GetCount() > 0)
4099 {
4100 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4101 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4102 {
4103 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4104 }
4105 }
4106
d2d0adc7 4107 // Get line height from first line, if any
d3b9f782 4108 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4109
4110 wxPoint linePos;
4111 int lineHeight wxDUMMY_INITIALIZE(0);
4112 if (line)
5d7836c4 4113 {
d2d0adc7
JS
4114 lineHeight = line->GetSize().y;
4115 linePos = line->GetPosition() + GetPosition();
5d7836c4 4116 }
d2d0adc7 4117 else
f089713f 4118 {
f089713f 4119 wxFont font;
44cc96a8
JS
4120 if (bulletAttr.HasFont() && GetBuffer())
4121 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4122 else
4123 font = (*wxNORMAL_FONT);
4124
ecb5fbf1 4125 wxCheckSetFont(dc, font);
f089713f 4126
d2d0adc7
JS
4127 lineHeight = dc.GetCharHeight();
4128 linePos = GetPosition();
4129 linePos.y += spaceBeforePara;
4130 }
f089713f 4131
d2d0adc7 4132 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4133
d2d0adc7
JS
4134 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4135 {
4136 if (wxRichTextBuffer::GetRenderer())
4137 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4138 }
3e541562
JS
4139 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4140 {
d2d0adc7
JS
4141 if (wxRichTextBuffer::GetRenderer())
4142 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4143 }
5d7836c4
JS
4144 else
4145 {
4146 wxString bulletText = GetBulletText();
3e541562 4147
d2d0adc7
JS
4148 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4149 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4150 }
4151 }
4152 }
7fe8059f 4153
5d7836c4
JS
4154 // Draw the range for each line, one object at a time.
4155
4156 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4157 while (node)
4158 {
4159 wxRichTextLine* line = node->GetData();
1e967276 4160 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4161
5d7836c4
JS
4162 // Lines are specified relative to the paragraph
4163
4164 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4165
7051fa41
JS
4166 // Don't draw if off the screen
4167 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4168 {
7051fa41
JS
4169 wxPoint objectPosition = linePosition;
4170 int maxDescent = line->GetDescent();
4171
4172 // Loop through objects until we get to the one within range
4173 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4174
7051fa41
JS
4175 int i = 0;
4176 while (node2)
5d7836c4 4177 {
7051fa41 4178 wxRichTextObject* child = node2->GetData();
5d7836c4 4179
cdaed652 4180 if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4181 {
7051fa41
JS
4182 // Draw this part of the line at the correct position
4183 wxRichTextRange objectRange(child->GetRange());
4184 objectRange.LimitTo(lineRange);
4185
4186 wxSize objectSize;
603f702b 4187 if (child->IsTopLevel())
7051fa41 4188 {
603f702b
JS
4189 objectSize = child->GetCachedSize();
4190 objectRange = child->GetOwnRange();
7051fa41
JS
4191 }
4192 else
7051fa41 4193 {
603f702b
JS
4194#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4195 if (i < (int) line->GetObjectSizes().GetCount())
4196 {
4197 objectSize.x = line->GetObjectSizes()[(size_t) i];
4198 }
4199 else
4200#endif
4201 {
4202 int descent = 0;
4203 child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition);
4204 }
7051fa41 4205 }
5d7836c4 4206
7051fa41
JS
4207 // Use the child object's width, but the whole line's height
4208 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
603f702b 4209 child->Draw(dc, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4210
7051fa41
JS
4211 objectPosition.x += objectSize.x;
4212 i ++;
4213 }
4214 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4215 // Can break out of inner loop now since we've passed this line's range
4216 break;
5d7836c4 4217
7051fa41
JS
4218 node2 = node2->GetNext();
4219 }
5d7836c4
JS
4220 }
4221
4222 node = node->GetNext();
7fe8059f 4223 }
5d7836c4
JS
4224
4225 return true;
4226}
4227
4f3d5bc0
JS
4228// Get the range width using partial extents calculated for the whole paragraph.
4229static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4230{
4231 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4232
affbfa1f
JS
4233 if (partialExtents.GetCount() < (size_t) range.GetLength())
4234 return 0;
4235
4f3d5bc0
JS
4236 int leftMostPos = 0;
4237 if (range.GetStart() - para.GetRange().GetStart() > 0)
4238 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4239
4240 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4241
4242 int w = rightMostPos - leftMostPos;
4243
4244 return w;
4245}
4246
5d7836c4 4247/// Lay the item out
38113684 4248bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
5d7836c4 4249{
cdaed652
VZ
4250 // Deal with floating objects firstly before the normal layout
4251 wxRichTextBuffer* buffer = GetBuffer();
4252 wxASSERT(buffer);
4253 wxRichTextFloatCollector* collector = buffer->GetFloatCollector();
4254 wxASSERT(collector);
4255 LayoutFloat(dc, rect, style, collector);
4256
24777478 4257 wxRichTextAttr attr = GetCombinedAttributes();
fe5aa22c 4258
169adfa9
JS
4259 // ClearLines();
4260
5d7836c4 4261 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4262 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4263 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4264 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4265 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4266 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4267
4268 int lineSpacing = 0;
4269
4270 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
8f0e4366 4271 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().Ok())
5d7836c4 4272 {
8f0e4366
JS
4273 wxCheckSetFont(dc, attr.GetFont());
4274 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
5d7836c4
JS
4275 }
4276
5d7836c4
JS
4277 // Start position for each line relative to the paragraph
4278 int startPositionFirstLine = leftIndent;
4279 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4280
4281 // If we have a bullet in this paragraph, the start position for the first line's text
4282 // is actually leftIndent + leftSubIndent.
fe5aa22c 4283 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4284 startPositionFirstLine = startPositionSubsequentLines;
4285
5d7836c4
JS
4286 long lastEndPos = GetRange().GetStart()-1;
4287 long lastCompletedEndPos = lastEndPos;
4288
4289 int currentWidth = 0;
4290 SetPosition(rect.GetPosition());
4291
4292 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4293 int lineHeight = 0;
4294 int maxWidth = 0;
603f702b 4295 int maxHeight = currentPosition.y;
476a319a 4296 int maxAscent = 0;
5d7836c4 4297 int maxDescent = 0;
5d7836c4 4298 int lineCount = 0;
cdaed652
VZ
4299 int lineAscent = 0;
4300 int lineDescent = 0;
5d7836c4 4301
2f45f554
JS
4302 wxRichTextObjectList::compatibility_iterator node;
4303
4304#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4305#if 0
4306 node = m_children.GetFirst();
4307 while (node)
4308 {
4309 wxRichTextObject* child = node->GetData();
4310 if (child->IsTopLevel())
4311 {
4312 //child->SetCachedSize(wxDefaultSize);
4313 wxRect availableChildRect = AdjustAvailableSpace(dc, GetBuffer(), GetAttributes(), child->GetAttributes(), rect);
4314
4315 // Hm, can't do this here, we surely need to take into account indents, margins, floating images etc.
4316 // So need to call layout lower down.
4317 child->Layout(dc, availableChildRect, style);
4318 }
4319
4320 node = node->GetNext();
4321 }
4322#endif
4323
2f45f554
JS
4324 wxUnusedVar(style);
4325 wxArrayInt partialExtents;
4326
4327 wxSize paraSize;
8aab23a1 4328 int paraDescent = 0;
2f45f554
JS
4329
4330 // This calculates the partial text extents
4331 GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4332#else
4333 node = m_children.GetFirst();
ecb5fbf1
JS
4334 while (node)
4335 {
4336 wxRichTextObject* child = node->GetData();
4337
603f702b 4338 //child->SetCachedSize(wxDefaultSize);
ecb5fbf1
JS
4339 child->Layout(dc, rect, style);
4340
4341 node = node->GetNext();
4342 }
4343
31778480
JS
4344#endif
4345
5d7836c4
JS
4346 // Split up lines
4347
4348 // We may need to go back to a previous child, in which case create the new line,
4349 // find the child corresponding to the start position of the string, and
4350 // continue.
4351
603f702b
JS
4352 wxRect availableRect;
4353
ecb5fbf1 4354 node = m_children.GetFirst();
5d7836c4
JS
4355 while (node)
4356 {
4357 wxRichTextObject* child = node->GetData();
4358
cdaed652 4359 // If floating, ignore. We already laid out floats.
603f702b
JS
4360 // Also ignore if empty object, except if we haven't got any
4361 // size yet.
4362 if (child->IsFloating() || !child->IsShown() ||
4363 (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4364 )
affbfa1f
JS
4365 {
4366 node = node->GetNext();
4367 continue;
4368 }
4369
5d7836c4
JS
4370 // If this is e.g. a composite text box, it will need to be laid out itself.
4371 // But if just a text fragment or image, for example, this will
4372 // do nothing. NB: won't we need to set the position after layout?
4373 // since for example if position is dependent on vertical line size, we
4374 // can't tell the position until the size is determined. So possibly introduce
4375 // another layout phase.
4376
5d7836c4
JS
4377 // We may only be looking at part of a child, if we searched back for wrapping
4378 // and found a suitable point some way into the child. So get the size for the fragment
4379 // if necessary.
3e541562 4380
ff76711f
JS
4381 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4382 long lastPosToUse = child->GetRange().GetEnd();
4383 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4384
ff76711f
JS
4385 if (lineBreakInThisObject)
4386 lastPosToUse = nextBreakPos;
5d7836c4
JS
4387
4388 wxSize childSize;
4389 int childDescent = 0;
3e541562 4390
603f702b
JS
4391 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4392 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4393 rect.width - startOffset - rightIndent, rect.height);
4394
4395 if (child->IsTopLevel())
4396 {
4397 wxSize oldSize = child->GetCachedSize();
4398
4399 child->Invalidate(wxRICHTEXT_ALL);
4400 child->SetPosition(wxPoint(0, 0));
4401
4402 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4403 // lays out the object again using the minimum size
4404 // The position will be determined by its location in its line,
4405 // and not by the child's actual position.
4406 child->LayoutToBestSize(dc, GetBuffer(),
4407 GetAttributes(), child->GetAttributes(), availableRect, style);
4408
4409 if (oldSize != child->GetCachedSize())
4410 {
4411 partialExtents.Clear();
4412
4413 // Recalculate the partial text extents since the child object changed size
4414 GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4415 }
4416 }
4417
4418 // Problem: we need to layout composites here for which we need the available width,
4419 // but we can't get the available width without using the float collector which
4420 // needs to know the object height.
4421
ff76711f 4422 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4423 {
4424 childSize = child->GetCachedSize();
4425 childDescent = child->GetDescent();
4426 }
4427 else
4f3d5bc0
JS
4428 {
4429#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4430 // Get height only, then the width using the partial extents
4431 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4432 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4433#else
ff76711f 4434 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
4f3d5bc0
JS
4435#endif
4436 }
ff76711f 4437
603f702b
JS
4438 bool doLoop = true;
4439 int loopIterations = 0;
4440
4441 // If there are nested objects that need to lay themselves out, we have to do this in a
4442 // loop because the height of the object may well depend on the available width.
4443 // And because of floating object positioning, the available width depends on the
4444 // height of the object and whether it will clash with the floating objects.
4445 // So, we see whether the available width changes due to the presence of floating images.
4446 // If it does, then we'll use the new restricted width to find the object height again.
4447 // If this causes another restriction in the available width, we'll try again, until
4448 // either we lose patience or the available width settles down.
4449 do
4450 {
4451 loopIterations ++;
4452
4453 wxRect oldAvailableRect = availableRect;
4454
4455 // Available width depends on the floating objects and the line height.
4456 // Note: the floating objects may be placed vertically along the two side of
4457 // buffer, so we may have different available line widths with different
4458 // [startY, endY]. So, we can't determine how wide the available
4459 // space is until we know the exact line height.
4460 lineDescent = wxMax(childDescent, maxDescent);
4461 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4462 lineHeight = lineDescent + lineAscent;
4463 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4464
4465 // Adjust availableRect to the space that is available when taking floating objects into account.
4466
4467 if (floatAvailableRect.x + startOffset > availableRect.x)
4468 {
4469 int newX = floatAvailableRect.x + startOffset;
4470 int newW = availableRect.width - (newX - availableRect.x);
4471 availableRect.x = newX;
4472 availableRect.width = newW;
4473 }
4474
4475 if (floatAvailableRect.width < availableRect.width)
4476 availableRect.width = floatAvailableRect.width;
4477
4478 currentPosition.x = availableRect.x - rect.x;
4479
4480 if (child->IsTopLevel() && loopIterations <= 20)
4481 {
4482 if (availableRect != oldAvailableRect)
4483 {
4484 wxSize oldSize = child->GetCachedSize();
4485
4486 //child->SetCachedSize(wxDefaultSize);
4487 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4488 // lays out the object again using the minimum size
4489 child->Invalidate(wxRICHTEXT_ALL);
4490 child->LayoutToBestSize(dc, GetBuffer(),
4491 GetAttributes(), child->GetAttributes(), availableRect, style);
4492 childSize = child->GetCachedSize();
4493 childDescent = child->GetDescent();
4494 //child->SetPosition(availableRect.GetPosition());
4495
4496 if (oldSize != child->GetCachedSize())
4497 {
4498 partialExtents.Clear();
4499
4500 // Recalculate the partial text extents since the child object changed size
4501 GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4502 }
cdaed652 4503
603f702b
JS
4504 // Go around the loop finding the available rect for the given floating objects
4505 }
4506 else
4507 doLoop = false;
4508 }
4509 else
4510 doLoop = false;
4511 }
4512 while (doLoop);
cdaed652 4513
ff76711f
JS
4514 // Cases:
4515 // 1) There was a line break BEFORE the natural break
4516 // 2) There was a line break AFTER the natural break
603f702b
JS
4517 // 3) It's the last line
4518 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4519
603f702b
JS
4520 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4521 ||
4522 (childSize.x + currentWidth > availableRect.width)
4523 ||
4524 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4525
4526 )
5d7836c4 4527 {
603f702b
JS
4528 if (child->IsTopLevel())
4529 {
4530 // We can move it to the correct position at this point
4531 child->Move(GetPosition() + wxPoint(currentWidth, currentPosition.y));
4532 }
4533
5d7836c4 4534 long wrapPosition = 0;
603f702b
JS
4535 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4536 wrapPosition = child->GetRange().GetEnd();
4537 else
5d7836c4
JS
4538
4539 // Find a place to wrap. This may walk back to previous children,
4540 // for example if a word spans several objects.
cdaed652
VZ
4541 // Note: one object must contains only one wxTextAtrr, so the line height will not
4542 // change inside one object. Thus, we can pass the remain line width to the
4543 // FindWrapPosition function.
603f702b 4544 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4545 {
4546 // If the function failed, just cut it off at the end of this child.
4547 wrapPosition = child->GetRange().GetEnd();
4548 }
4549
4550 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4551 if (wrapPosition <= lastCompletedEndPos)
4552 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4553
603f702b
JS
4554 // Line end position shouldn't be the same as the end, or greater.
4555 if (wrapPosition >= GetRange().GetEnd())
4556 wrapPosition = GetRange().GetEnd()-1;
4557
5d7836c4 4558 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4559
5d7836c4
JS
4560 // Let's find the actual size of the current line now
4561 wxSize actualSize;
4562 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4563
4ab8a5e2
JS
4564 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4565 /// for the fragment we're about to add.
4566 childDescent = maxDescent;
4567
4f3d5bc0 4568#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4569 if (!child->IsEmpty())
4570 {
4571 // Get height only, then the width using the partial extents
4572 GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4573 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4574 }
4575 else
4f3d5bc0 4576#endif
603f702b 4577 GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED);
4f3d5bc0 4578
5d7836c4 4579 currentWidth = actualSize.x;
5d7836c4 4580 maxDescent = wxMax(childDescent, maxDescent);
476a319a
JS
4581 maxAscent = wxMax(actualSize.y-childDescent, maxAscent);
4582 lineHeight = maxDescent + maxAscent;
7fe8059f 4583
603f702b
JS
4584 if (lineHeight == 0 && GetBuffer())
4585 {
4586 wxFont font(GetBuffer()->GetFontTable().FindFont(attr));
4587 wxCheckSetFont(dc, font);
4588 lineHeight = dc.GetCharHeight();
4589 }
4590
4591 if (maxDescent == 0)
4592 {
4593 int w, h;
4594 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
4595 }
4596
5d7836c4 4597 // Add a new line
1e967276 4598 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 4599
1e967276
JS
4600 // Set relative range so we won't have to change line ranges when paragraphs are moved
4601 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
4602 line->SetPosition(currentPosition);
4603 line->SetSize(wxSize(currentWidth, lineHeight));
4604 line->SetDescent(maxDescent);
4605
603f702b
JS
4606 maxHeight = currentPosition.y + lineHeight;
4607
5d7836c4
JS
4608 // Now move down a line. TODO: add margins, spacing
4609 currentPosition.y += lineHeight;
4610 currentPosition.y += lineSpacing;
5d7836c4 4611 maxDescent = 0;
476a319a 4612 maxAscent = 0;
603f702b
JS
4613 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4614 currentWidth = 0;
7fe8059f 4615
5d7836c4
JS
4616 lineCount ++;
4617
4618 // TODO: account for zero-length objects, such as fields
603f702b 4619 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 4620
5d7836c4
JS
4621 lastEndPos = wrapPosition;
4622 lastCompletedEndPos = lastEndPos;
4623
4624 lineHeight = 0;
4625
603f702b
JS
4626 if (wrapPosition < GetRange().GetEnd()-1)
4627 {
4628 // May need to set the node back to a previous one, due to searching back in wrapping
4629 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
4630 if (childAfterWrapPosition)
4631 node = m_children.Find(childAfterWrapPosition);
4632 else
4633 node = node->GetNext();
4634 }
5d7836c4
JS
4635 else
4636 node = node->GetNext();
603f702b
JS
4637
4638 // Apply paragraph styles such as alignment to the wrapped line
4639 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
4640 }
4641 else
4642 {
4643 // We still fit, so don't add a line, and keep going
4644 currentWidth += childSize.x;
5d7836c4 4645 maxDescent = wxMax(childDescent, maxDescent);
476a319a
JS
4646 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
4647 lineHeight = maxDescent + maxAscent;
5d7836c4 4648
603f702b 4649 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
4650 lastEndPos = child->GetRange().GetEnd();
4651
4652 node = node->GetNext();
4653 }
4654 }
4655
603f702b
JS
4656 wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4657
4658#if 0
5d7836c4
JS
4659 // Add the last line - it's the current pos -> last para pos
4660 // Substract -1 because the last position is always the end-paragraph position.
4661 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
4662 {
603f702b 4663 currentPosition.x = availableRect.x - rect.x;
5d7836c4 4664
1e967276
JS
4665 wxRichTextLine* line = AllocateLine(lineCount);
4666
4667 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
4668
4669 // Set relative range so we won't have to change line ranges when paragraphs are moved
4670 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
7fe8059f 4671
5d7836c4
JS
4672 line->SetPosition(currentPosition);
4673
44cc96a8 4674 if (lineHeight == 0 && GetBuffer())
5d7836c4 4675 {
44cc96a8 4676 wxFont font(GetBuffer()->GetFontTable().FindFont(attr));
ecb5fbf1 4677 wxCheckSetFont(dc, font);
5d7836c4
JS
4678 lineHeight = dc.GetCharHeight();
4679 }
4680 if (maxDescent == 0)
4681 {
4682 int w, h;
4683 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
4684 }
4685
4686 line->SetSize(wxSize(currentWidth, lineHeight));
4687 line->SetDescent(maxDescent);
603f702b 4688 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
4689 currentPosition.y += lineHeight;
4690 currentPosition.y += lineSpacing;
4691 lineCount ++;
5d7836c4 4692 }
603f702b 4693#endif
5d7836c4 4694
1e967276
JS
4695 // Remove remaining unused line objects, if any
4696 ClearUnusedLines(lineCount);
4697
603f702b
JS
4698 // We need to add back the margins etc.
4699 {
4700 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4701 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
4702 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
4703 SetCachedSize(marginRect.GetSize());
4704 }
4705
4706 // The maximum size is the length of the paragraph stretched out into a line.
4707 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4708 // this size. TODO: take into account line breaks.
4709 {
4710 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4711 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x, currentPosition.y + spaceAfterPara));
4712 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
4713 SetMaxSize(marginRect.GetSize());
4714 }
4715
4716 // Find the greatest minimum size. Currently we only look at non-text objects,
4717 // which isn't ideal but it would be slow to find the maximum word width to
4718 // use as the minimum.
4719 {
4720 int minWidth = 0;
4721 node = m_children.GetFirst();
4722 while (node)
4723 {
4724 wxRichTextObject* child = node->GetData();
4725
4726 // If floating, ignore. We already laid out floats.
4727 // Also ignore if empty object, except if we haven't got any
4728 // size yet.
4729 if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
4730 {
4731 if (child->GetCachedSize().x > minWidth)
4732 minWidth = child->GetMinSize().x;
4733 }
4734 node = node->GetNext();
4735 }
5d7836c4 4736
603f702b
JS
4737 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4738 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
4739 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
4740 SetMinSize(marginRect.GetSize());
4741 }
5d7836c4 4742
5d7836c4 4743
2f45f554
JS
4744#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4745#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4746 // Use the text extents to calculate the size of each fragment in each line
4747 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
4748 while (lineNode)
4749 {
4750 wxRichTextLine* line = lineNode->GetData();
4751 wxRichTextRange lineRange = line->GetAbsoluteRange();
4752
4753 // Loop through objects until we get to the one within range
4754 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4755
4756 while (node2)
4757 {
4758 wxRichTextObject* child = node2->GetData();
4759
affbfa1f 4760 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
4761 {
4762 wxRichTextRange rangeToUse = lineRange;
4763 rangeToUse.LimitTo(child->GetRange());
4764
4765 // Find the size of the child from the text extents, and store in an array
4766 // for drawing later
4767 int left = 0;
4768 if (rangeToUse.GetStart() > GetRange().GetStart())
4769 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
4770 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
4771 int sz = right - left;
4772 line->GetObjectSizes().Add(sz);
4773 }
4774 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4775 // Can break out of inner loop now since we've passed this line's range
4776 break;
4777
4778 node2 = node2->GetNext();
4779 }
4780
4781 lineNode = lineNode->GetNext();
4782 }
4783#endif
4784#endif
4785
5d7836c4
JS
4786 return true;
4787}
4788
603f702b 4789#if 0
5d7836c4 4790/// Apply paragraph styles, such as centering, to wrapped lines
603f702b 4791/// TODO: take into account box attributes
24777478 4792void wxRichTextParagraph::ApplyParagraphStyle(const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5d7836c4 4793{
fe5aa22c 4794 if (!attr.HasAlignment())
5d7836c4
JS
4795 return;
4796
4797 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4798 while (node)
4799 {
4800 wxRichTextLine* line = node->GetData();
4801
4802 wxPoint pos = line->GetPosition();
4803 wxSize size = line->GetSize();
4804
4805 // centering, right-justification
fe5aa22c 4806 if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
5d7836c4 4807 {
43a0d1e1 4808 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
603f702b
JS
4809 // Subtract paragraph position because lines are relative to
4810 // the paragraph.
4811 pos.x = rect.x - GetPosition().x + (rect.GetWidth() - rightIndent - size.x)/2;
5d7836c4
JS
4812 line->SetPosition(pos);
4813 }
fe5aa22c 4814 else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
5d7836c4 4815 {
43a0d1e1 4816 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
603f702b
JS
4817 // Subtract paragraph position because lines are relative to
4818 // the paragraph.
4819 pos.x = (rect.x - GetPosition().x) + rect.GetWidth() - size.x - rightIndent;
5d7836c4
JS
4820 line->SetPosition(pos);
4821 }
4822
4823 node = node->GetNext();
4824 }
4825}
603f702b
JS
4826#endif
4827
4828/// Apply paragraph styles, such as centering, to wrapped lines
4829/// TODO: take into account box attributes, possibly
4830void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
4831{
4832 if (!attr.HasAlignment())
4833 return;
4834
4835 wxPoint pos = line->GetPosition();
4836 wxSize size = line->GetSize();
4837
4838 // centering, right-justification
4839 if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
4840 {
4841 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4842 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
4843 line->SetPosition(pos);
4844 }
4845 else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
4846 {
4847 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4848 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
4849 line->SetPosition(pos);
4850 }
4851}
5d7836c4
JS
4852
4853/// Insert text at the given position
4854bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
4855{
4856 wxRichTextObject* childToUse = NULL;
09f14108 4857 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
4858
4859 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4860 while (node)
4861 {
4862 wxRichTextObject* child = node->GetData();
4863 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
4864 {
4865 childToUse = child;
4866 nodeToUse = node;
4867 break;
4868 }
4869
4870 node = node->GetNext();
4871 }
4872
4873 if (childToUse)
4874 {
4875 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
4876 if (textObject)
4877 {
4878 int posInString = pos - textObject->GetRange().GetStart();
4879
4880 wxString newText = textObject->GetText().Mid(0, posInString) +
4881 text + textObject->GetText().Mid(posInString);
4882 textObject->SetText(newText);
4883
28f92d74 4884 int textLength = text.length();
5d7836c4
JS
4885
4886 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
4887 textObject->GetRange().GetEnd() + textLength));
4888
4889 // Increment the end range of subsequent fragments in this paragraph.
4890 // We'll set the paragraph range itself at a higher level.
4891
4892 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
4893 while (node)
4894 {
4895 wxRichTextObject* child = node->GetData();
4896 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
4897 textObject->GetRange().GetEnd() + textLength));
7fe8059f 4898
5d7836c4
JS
4899 node = node->GetNext();
4900 }
4901
4902 return true;
4903 }
4904 else
4905 {
4906 // TODO: if not a text object, insert at closest position, e.g. in front of it
4907 }
4908 }
4909 else
4910 {
4911 // Add at end.
4912 // Don't pass parent initially to suppress auto-setting of parent range.
4913 // We'll do that at a higher level.
4914 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
4915
4916 AppendChild(textObject);
4917 return true;
4918 }
4919
4920 return false;
4921}
4922
4923void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
4924{
bec80f4f 4925 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
4926}
4927
4928/// Clear the cached lines
4929void wxRichTextParagraph::ClearLines()
4930{
4931 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
4932}
4933
4934/// Get/set the object size for the given range. Returns false if the range
4935/// is invalid for this object.
31778480 4936bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
4937{
4938 if (!range.IsWithin(GetRange()))
4939 return false;
4940
4941 if (flags & wxRICHTEXT_UNFORMATTED)
4942 {
4943 // Just use unformatted data, assume no line breaks
4944 // TODO: take into account line breaks
4945
4946 wxSize sz;
4947
31778480
JS
4948 wxArrayInt childExtents;
4949 wxArrayInt* p;
4950 if (partialExtents)
4951 p = & childExtents;
4952 else
4953 p = NULL;
4954
5d7836c4
JS
4955 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4956 while (node)
4957 {
31778480 4958
5d7836c4
JS
4959 wxRichTextObject* child = node->GetData();
4960 if (!child->GetRange().IsOutside(range))
4961 {
cdaed652
VZ
4962 // Floating objects have a zero size within the paragraph.
4963 if (child->IsFloating())
4964 {
4965 if (partialExtents)
4966 {
4967 int lastSize;
4968 if (partialExtents->GetCount() > 0)
4969 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
4970 else
4971 lastSize = 0;
4972
4973 partialExtents->Add(0 /* zero size */ + lastSize);
4974 }
4975 }
4976 else
4977 {
603f702b 4978 wxSize childSize;
4f3d5bc0 4979
603f702b
JS
4980 wxRichTextRange rangeToUse = range;
4981 rangeToUse.LimitTo(child->GetRange());
4982#if 0
4983 if (child->IsTopLevel())
4984 rangeToUse = child->GetOwnRange();
4985#endif
4986 int childDescent = 0;
31778480 4987
603f702b
JS
4988 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
4989 // but it's only going to be used after caching has taken place.
4990 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 4991 {
603f702b
JS
4992 childDescent = child->GetDescent();
4993 childSize = child->GetCachedSize();
2f45f554 4994
603f702b
JS
4995 sz.y = wxMax(sz.y, childSize.y);
4996 sz.x += childSize.x;
4997 descent = wxMax(descent, childDescent);
4998 }
4999 else if (child->IsTopLevel())
31778480 5000 {
603f702b
JS
5001 childDescent = child->GetDescent();
5002 childSize = child->GetCachedSize();
31778480 5003
603f702b
JS
5004 sz.y = wxMax(sz.y, childSize.y);
5005 sz.x += childSize.x;
5006 descent = wxMax(descent, childDescent);
5007 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5008 {
603f702b
JS
5009 child->SetCachedSize(childSize);
5010 child->SetDescent(childDescent);
31778480 5011 }
31778480 5012
603f702b
JS
5013 if (partialExtents)
5014 {
5015 int lastSize;
5016 if (partialExtents->GetCount() > 0)
5017 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5018 else
5019 lastSize = 0;
5020
5021 partialExtents->Add(childSize.x + lastSize);
5022 }
5023 }
5024 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p))
5025 {
5026 sz.y = wxMax(sz.y, childSize.y);
5027 sz.x += childSize.x;
5028 descent = wxMax(descent, childDescent);
5029
5030 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5031 {
5032 child->SetCachedSize(childSize);
5033 child->SetDescent(childDescent);
5034 }
5035
5036 if (partialExtents)
5037 {
5038 int lastSize;
5039 if (partialExtents->GetCount() > 0)
5040 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5041 else
5042 lastSize = 0;
5043
5044 size_t i;
5045 for (i = 0; i < childExtents.GetCount(); i++)
5046 {
5047 partialExtents->Add(childExtents[i] + lastSize);
5048 }
5049 }
5050 }
5051 }
5052
5053 if (p)
5054 p->Clear();
5d7836c4
JS
5055 }
5056
5057 node = node->GetNext();
5058 }
5059 size = sz;
5060 }
5061 else
5062 {
5063 // Use formatted data, with line breaks
5064 wxSize sz;
5065
5066 // We're going to loop through each line, and then for each line,
5067 // call GetRangeSize for the fragment that comprises that line.
5068 // Only we have to do that multiple times within the line, because
5069 // the line may be broken into pieces. For now ignore line break commands
5070 // (so we can assume that getting the unformatted size for a fragment
5071 // within a line is the actual size)
5072
5073 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5074 while (node)
5075 {
5076 wxRichTextLine* line = node->GetData();
1e967276
JS
5077 wxRichTextRange lineRange = line->GetAbsoluteRange();
5078 if (!lineRange.IsOutside(range))
5d7836c4
JS
5079 {
5080 wxSize lineSize;
7fe8059f 5081
5d7836c4
JS
5082 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5083 while (node2)
5084 {
5085 wxRichTextObject* child = node2->GetData();
7fe8059f 5086
cdaed652 5087 if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
5d7836c4 5088 {
1e967276 5089 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5090 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5091 if (child->IsTopLevel())
5092 rangeToUse = child->GetOwnRange();
7fe8059f 5093
5d7836c4
JS
5094 wxSize childSize;
5095 int childDescent = 0;
0f1fbeb8 5096 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y)))
5d7836c4
JS
5097 {
5098 lineSize.y = wxMax(lineSize.y, childSize.y);
5099 lineSize.x += childSize.x;
5100 }
5101 descent = wxMax(descent, childDescent);
5102 }
7fe8059f 5103
5d7836c4
JS
5104 node2 = node2->GetNext();
5105 }
5106
5107 // Increase size by a line (TODO: paragraph spacing)
5108 sz.y += lineSize.y;
5109 sz.x = wxMax(sz.x, lineSize.x);
5110 }
5111 node = node->GetNext();
5112 }
5113 size = sz;
5114 }
5115 return true;
5116}
5117
5118/// Finds the absolute position and row height for the given character position
5119bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
5120{
5121 if (index == -1)
5122 {
5123 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5124 if (line)
5125 *height = line->GetSize().y;
5126 else
5127 *height = dc.GetCharHeight();
5128
5129 // -1 means 'the start of the buffer'.
5130 pt = GetPosition();
5131 if (line)
5132 pt = pt + line->GetPosition();
5133
5d7836c4
JS
5134 return true;
5135 }
5136
5137 // The final position in a paragraph is taken to mean the position
5138 // at the start of the next paragraph.
5139 if (index == GetRange().GetEnd())
5140 {
5141 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5142 wxASSERT( parent != NULL );
5143
5144 // Find the height at the next paragraph, if any
5145 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5146 if (line)
5147 {
5148 *height = line->GetSize().y;
5149 pt = line->GetAbsolutePosition();
5150 }
5151 else
5152 {
5153 *height = dc.GetCharHeight();
5154 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5155 pt = wxPoint(indent, GetCachedSize().y);
5156 }
5157
5158 return true;
5159 }
5160
5161 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5162 return false;
5163
5164 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5165 while (node)
5166 {
5167 wxRichTextLine* line = node->GetData();
1e967276
JS
5168 wxRichTextRange lineRange = line->GetAbsoluteRange();
5169 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5170 {
5171 // If this is the last point in the line, and we're forcing the
5172 // returned value to be the start of the next line, do the required
5173 // thing.
1e967276 5174 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5175 {
5176 if (node->GetNext())
5177 {
5178 wxRichTextLine* nextLine = node->GetNext()->GetData();
5179 *height = nextLine->GetSize().y;
5180 pt = nextLine->GetAbsolutePosition();
5181 return true;
5182 }
5183 }
5184
5185 pt.y = line->GetPosition().y + GetPosition().y;
5186
1e967276 5187 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5188 wxSize rangeSize;
5189 int descent = 0;
5190
5191 // We find the size of the line up to this point,
5192 // then we can add this size to the line start position and
5193 // paragraph start position to find the actual position.
5194
7f0d9d71 5195 if (GetRangeSize(r, rangeSize, descent, dc, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5196 {
5197 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5198 *height = line->GetSize().y;
5199
5200 return true;
5201 }
5202
5203 }
5204
5205 node = node->GetNext();
5206 }
5207
5208 return false;
5209}
5210
5211/// Hit-testing: returns a flag indicating hit test details, plus
5212/// information about position
603f702b 5213int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5214{
603f702b
JS
5215 if (!IsShown())
5216 return wxRICHTEXT_HITTEST_NONE;
5217
5218 // If we're in the top-level container, then we can return
5219 // a suitable hit test code even if the point is outside the container area,
5220 // so that we can position the caret sensibly even if we don't
5221 // click on valid content. If we're not at the top-level, and the point
5222 // is not within this paragraph object, then we don't want to stop more
5223 // precise hit-testing from working prematurely, so return immediately.
5224 // NEW STRATEGY: use the parent boundary to test whether we're in the
5225 // right region, not the paragraph, since the paragraph may be positioned
5226 // some way in from where the user clicks.
5227 {
5228 long tmpPos;
5229 wxRichTextObject* tempObj, *tempContextObj;
5230 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
5231 return wxRICHTEXT_HITTEST_NONE;
5232 }
5233
5234 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5235 while (objNode)
5236 {
5237 wxRichTextObject* child = objNode->GetData();
5238 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0))
5239 {
5240 {
5241 int hitTest = child->HitTest(dc, pt, textPosition, obj, contextObj);
5242 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5243 return hitTest;
5244 }
5245 }
5246
5247 objNode = objNode->GetNext();
5248 }
5249
5d7836c4
JS
5250 wxPoint paraPos = GetPosition();
5251
5252 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5253 while (node)
5254 {
5255 wxRichTextLine* line = node->GetData();
5256 wxPoint linePos = paraPos + line->GetPosition();
5257 wxSize lineSize = line->GetSize();
1e967276 5258 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5259
62381daa 5260 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5261 {
5262 if (pt.x < linePos.x)
5263 {
1e967276 5264 textPosition = lineRange.GetStart();
603f702b
JS
5265 *obj = FindObjectAtPosition(textPosition);
5266 *contextObj = GetContainer();
f262b25c 5267 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5268 }
5269 else if (pt.x >= (linePos.x + lineSize.x))
5270 {
1e967276 5271 textPosition = lineRange.GetEnd();
603f702b
JS
5272 *obj = FindObjectAtPosition(textPosition);
5273 *contextObj = GetContainer();
f262b25c 5274 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5275 }
5276 else
5277 {
2f45f554
JS
5278#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5279 wxArrayInt partialExtents;
5280
5281 wxSize paraSize;
5282 int paraDescent;
5283
5284 // This calculates the partial text extents
5285 GetRangeSize(lineRange, paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), & partialExtents);
5286
5287 int lastX = linePos.x;
5288 size_t i;
5289 for (i = 0; i < partialExtents.GetCount(); i++)
5290 {
5291 int nextX = partialExtents[i] + linePos.x;
5292
5293 if (pt.x >= lastX && pt.x <= nextX)
5294 {
5295 textPosition = i + lineRange.GetStart(); // minus 1?
5296
603f702b
JS
5297 *obj = FindObjectAtPosition(textPosition);
5298 *contextObj = GetContainer();
5299
2f45f554
JS
5300 // So now we know it's between i-1 and i.
5301 // Let's see if we can be more precise about
5302 // which side of the position it's on.
5303
cdaed652 5304 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5305 if (pt.x >= midPoint)
5306 return wxRICHTEXT_HITTEST_AFTER;
5307 else
5308 return wxRICHTEXT_HITTEST_BEFORE;
5309 }
5310
5311 lastX = nextX;
5312 }
5313#else
5d7836c4
JS
5314 long i;
5315 int lastX = linePos.x;
1e967276 5316 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5317 {
5318 wxSize childSize;
5319 int descent = 0;
7fe8059f 5320
1e967276 5321 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5322
7f0d9d71 5323 GetRangeSize(rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5324
5325 int nextX = childSize.x + linePos.x;
5326
5327 if (pt.x >= lastX && pt.x <= nextX)
5328 {
5329 textPosition = i;
5330
603f702b
JS
5331 *obj = FindObjectAtPosition(textPosition);
5332 *contextObj = GetContainer();
5333
5d7836c4
JS
5334 // So now we know it's between i-1 and i.
5335 // Let's see if we can be more precise about
5336 // which side of the position it's on.
5337
cdaed652 5338 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5339 if (pt.x >= midPoint)
5340 return wxRICHTEXT_HITTEST_AFTER;
5341 else
5342 return wxRICHTEXT_HITTEST_BEFORE;
5343 }
5344 else
5345 {
5346 lastX = nextX;
5347 }
5348 }
2f45f554 5349#endif
5d7836c4
JS
5350 }
5351 }
7fe8059f 5352
5d7836c4
JS
5353 node = node->GetNext();
5354 }
5355
5356 return wxRICHTEXT_HITTEST_NONE;
5357}
5358
5359/// Split an object at this position if necessary, and return
5360/// the previous object, or NULL if inserting at beginning.
5361wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5362{
5363 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5364 while (node)
5365 {
5366 wxRichTextObject* child = node->GetData();
5367
5368 if (pos == child->GetRange().GetStart())
5369 {
5370 if (previousObject)
4d551ad5
JS
5371 {
5372 if (node->GetPrevious())
5373 *previousObject = node->GetPrevious()->GetData();
5374 else
5375 *previousObject = NULL;
5376 }
5d7836c4
JS
5377
5378 return child;
5379 }
5380
5381 if (child->GetRange().Contains(pos))
5382 {
5383 // This should create a new object, transferring part of
5384 // the content to the old object and the rest to the new object.
5385 wxRichTextObject* newObject = child->DoSplit(pos);
5386
5387 // If we couldn't split this object, just insert in front of it.
5388 if (!newObject)
5389 {
5390 // Maybe this is an empty string, try the next one
5391 // return child;
5392 }
5393 else
5394 {
5395 // Insert the new object after 'child'
5396 if (node->GetNext())
5397 m_children.Insert(node->GetNext(), newObject);
5398 else
5399 m_children.Append(newObject);
5400 newObject->SetParent(this);
5401
5402 if (previousObject)
5403 *previousObject = child;
5404
5405 return newObject;
5406 }
5407 }
5408
5409 node = node->GetNext();
5410 }
5411 if (previousObject)
5412 *previousObject = NULL;
5413 return NULL;
5414}
5415
5416/// Move content to a list from obj on
5417void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5418{
5419 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5420 while (node)
5421 {
5422 wxRichTextObject* child = node->GetData();
5423 list.Append(child);
5424
5425 wxRichTextObjectList::compatibility_iterator oldNode = node;
5426
5427 node = node->GetNext();
5428
5429 m_children.DeleteNode(oldNode);
5430 }
5431}
5432
5433/// Add content back from list
5434void wxRichTextParagraph::MoveFromList(wxList& list)
5435{
09f14108 5436 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5437 {
5438 AppendChild((wxRichTextObject*) node->GetData());
5439 }
5440}
5441
5442/// Calculate range
5443void wxRichTextParagraph::CalculateRange(long start, long& end)
5444{
5445 wxRichTextCompositeObject::CalculateRange(start, end);
5446
5447 // Add one for end of paragraph
5448 end ++;
5449
5450 m_range.SetRange(start, end);
5451}
5452
5453/// Find the object at the given position
5454wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5455{
5456 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5457 while (node)
5458 {
5459 wxRichTextObject* obj = node->GetData();
603f702b
JS
5460 if (obj->GetRange().Contains(position) ||
5461 obj->GetRange().GetStart() == position ||
5462 obj->GetRange().GetEnd() == position)
5d7836c4 5463 return obj;
7fe8059f 5464
5d7836c4
JS
5465 node = node->GetNext();
5466 }
5467 return NULL;
5468}
5469
5470/// Get the plain text searching from the start or end of the range.
5471/// The resulting string may be shorter than the range given.
5472bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5473{
5474 text = wxEmptyString;
5475
5476 if (fromStart)
5477 {
5478 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5479 while (node)
5480 {
5481 wxRichTextObject* obj = node->GetData();
5482 if (!obj->GetRange().IsOutside(range))
5483 {
5484 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5485 if (textObj)
5486 {
5487 text += textObj->GetTextForRange(range);
5488 }
5489 else
043c0d58
JS
5490 {
5491 text += wxT(" ");
5492 }
5d7836c4
JS
5493 }
5494
5495 node = node->GetNext();
5496 }
5497 }
5498 else
5499 {
5500 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5501 while (node)
5502 {
5503 wxRichTextObject* obj = node->GetData();
5504 if (!obj->GetRange().IsOutside(range))
5505 {
5506 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5507 if (textObj)
5508 {
5509 text = textObj->GetTextForRange(range) + text;
5510 }
5511 else
043c0d58
JS
5512 {
5513 text = wxT(" ") + text;
5514 }
5d7836c4
JS
5515 }
5516
5517 node = node->GetPrevious();
5518 }
5519 }
5520
5521 return true;
5522}
5523
5524/// Find a suitable wrap position.
31778480 5525bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5526{
72945e24
JS
5527 if (range.GetLength() <= 0)
5528 return false;
5529
5d7836c4
JS
5530 // Find the first position where the line exceeds the available space.
5531 wxSize sz;
5d7836c4 5532 long breakPosition = range.GetEnd();
ecb5fbf1 5533
31778480
JS
5534#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5535 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 5536 {
31778480 5537 int widthBefore;
5d7836c4 5538
31778480
JS
5539 if (range.GetStart() > GetRange().GetStart())
5540 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5541 else
5542 widthBefore = 0;
5543
5544 size_t i;
43a0d1e1 5545 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 5546 {
31778480 5547 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 5548
72945e24 5549 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 5550 {
31778480
JS
5551 breakPosition = i-1;
5552 break;
ecb5fbf1 5553 }
5d7836c4 5554 }
31778480
JS
5555 }
5556 else
5557#endif
5558 {
5559 // Binary chop for speed
5560 long minPos = range.GetStart();
5561 long maxPos = range.GetEnd();
5562 while (true)
ecb5fbf1 5563 {
31778480
JS
5564 if (minPos == maxPos)
5565 {
5566 int descent = 0;
5567 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 5568
31778480
JS
5569 if (sz.x > availableSpace)
5570 breakPosition = minPos - 1;
5571 break;
5572 }
5573 else if ((maxPos - minPos) == 1)
ecb5fbf1 5574 {
31778480
JS
5575 int descent = 0;
5576 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
5577
5578 if (sz.x > availableSpace)
5579 breakPosition = minPos - 1;
5580 else
5581 {
5582 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
5583 if (sz.x > availableSpace)
5584 breakPosition = maxPos-1;
5585 }
5586 break;
ecb5fbf1
JS
5587 }
5588 else
5589 {
31778480
JS
5590 long nextPos = minPos + ((maxPos - minPos) / 2);
5591
5592 int descent = 0;
5593 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
5594
5595 if (sz.x > availableSpace)
5596 {
5597 maxPos = nextPos;
5598 }
5599 else
5600 {
5601 minPos = nextPos;
5602 }
ecb5fbf1
JS
5603 }
5604 }
5d7836c4
JS
5605 }
5606
5607 // Now we know the last position on the line.
5608 // Let's try to find a word break.
5609
5610 wxString plainText;
5611 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
5612 {
ff76711f
JS
5613 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
5614 if (newLinePos != wxNOT_FOUND)
5d7836c4 5615 {
ff76711f
JS
5616 breakPosition = wxMax(0, range.GetStart() + newLinePos);
5617 }
5618 else
5619 {
5620 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
5621 int tabPos = plainText.Find(wxT('\t'), true);
5622 int pos = wxMax(spacePos, tabPos);
5623 if (pos != wxNOT_FOUND)
ff76711f 5624 {
31002e44 5625 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
5626 breakPosition = breakPosition - positionsFromEndOfString;
5627 }
5d7836c4
JS
5628 }
5629 }
5630
5631 wrapPosition = breakPosition;
5632
5633 return true;
5634}
5635
5636/// Get the bullet text for this paragraph.
5637wxString wxRichTextParagraph::GetBulletText()
5638{
5639 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
5640 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
5641 return wxEmptyString;
5642
5643 int number = GetAttributes().GetBulletNumber();
5644
5645 wxString text;
d2d0adc7 5646 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
5647 {
5648 text.Printf(wxT("%d"), number);
5649 }
5650 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
5651 {
5652 // TODO: Unicode, and also check if number > 26
5653 text.Printf(wxT("%c"), (wxChar) (number+64));
5654 }
5655 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
5656 {
5657 // TODO: Unicode, and also check if number > 26
5658 text.Printf(wxT("%c"), (wxChar) (number+96));
5659 }
5660 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
5661 {
59509217 5662 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
5663 }
5664 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
5665 {
59509217
JS
5666 text = wxRichTextDecimalToRoman(number);
5667 text.MakeLower();
5d7836c4
JS
5668 }
5669 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
5670 {
d2d0adc7
JS
5671 text = GetAttributes().GetBulletText();
5672 }
3e541562 5673
d2d0adc7
JS
5674 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
5675 {
5676 // The outline style relies on the text being computed statically,
5677 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5678 // should be stored in the attributes; if not, just use the number for this
5679 // level, as previously computed.
5680 if (!GetAttributes().GetBulletText().IsEmpty())
5681 text = GetAttributes().GetBulletText();
5d7836c4
JS
5682 }
5683
5684 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
5685 {
5686 text = wxT("(") + text + wxT(")");
5687 }
d2d0adc7
JS
5688 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
5689 {
5690 text = text + wxT(")");
5691 }
5692
5d7836c4
JS
5693 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
5694 {
5695 text += wxT(".");
5696 }
5697
5698 return text;
5699}
5700
1e967276
JS
5701/// Allocate or reuse a line object
5702wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
5703{
5704 if (pos < (int) m_cachedLines.GetCount())
5705 {
5706 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
5707 line->Init(this);
5708 return line;
5709 }
5710 else
5711 {
5712 wxRichTextLine* line = new wxRichTextLine(this);
5713 m_cachedLines.Append(line);
5714 return line;
5715 }
5716}
5717
5718/// Clear remaining unused line objects, if any
5719bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
5720{
5721 int cachedLineCount = m_cachedLines.GetCount();
5722 if ((int) cachedLineCount > lineCount)
5723 {
5724 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
5725 {
5726 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
5727 wxRichTextLine* line = node->GetData();
5728 m_cachedLines.Erase(node);
5729 delete line;
5730 }
5731 }
5732 return true;
5733}
5734
fe5aa22c
JS
5735/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5736/// retrieve the actual style.
603f702b 5737wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 5738{
24777478 5739 wxRichTextAttr attr;
603f702b 5740 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
5741 if (buf)
5742 {
5743 attr = buf->GetBasicStyle();
603f702b
JS
5744 if (!includingBoxAttr)
5745 {
5746 attr.GetTextBoxAttr().Reset();
5747 // The background colour will be painted by the container, and we don't
5748 // want to unnecessarily overwrite the background when we're drawing text
5749 // because this may erase the guideline (which appears just under the text
5750 // if there's no padding).
5751 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
5752 }
fe5aa22c
JS
5753 wxRichTextApplyStyle(attr, GetAttributes());
5754 }
5755 else
5756 attr = GetAttributes();
5757
5758 wxRichTextApplyStyle(attr, contentStyle);
5759 return attr;
5760}
5761
5762/// Get combined attributes of the base style and paragraph style.
603f702b 5763wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 5764{
24777478 5765 wxRichTextAttr attr;
603f702b 5766 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
5767 if (buf)
5768 {
5769 attr = buf->GetBasicStyle();
603f702b
JS
5770 if (!includingBoxAttr)
5771 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
5772 wxRichTextApplyStyle(attr, GetAttributes());
5773 }
5774 else
5775 attr = GetAttributes();
5776
5777 return attr;
5778}
5d7836c4 5779
603f702b 5780// Create default tabstop array
cfa3b256
JS
5781void wxRichTextParagraph::InitDefaultTabs()
5782{
5783 // create a default tab list at 10 mm each.
5784 for (int i = 0; i < 20; ++i)
5785 {
5786 sm_defaultTabs.Add(i*100);
5787 }
5788}
5789
603f702b 5790// Clear default tabstop array
cfa3b256
JS
5791void wxRichTextParagraph::ClearDefaultTabs()
5792{
5793 sm_defaultTabs.Clear();
5794}
5795
cdaed652
VZ
5796void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
5797{
5798 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
5799 while (node)
5800 {
bec80f4f 5801 wxRichTextObject* anchored = node->GetData();
cdaed652
VZ
5802 if (anchored && anchored->IsFloating())
5803 {
5804 wxSize size;
5805 int descent, x = 0;
5806 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, style);
bec80f4f 5807
24777478 5808 int offsetY = 0;
603f702b 5809 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
5810 {
5811 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5812 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
5813 {
5814 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
5815 }
5816 }
bec80f4f 5817
24777478 5818 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 5819
cdaed652 5820 /* Update the offset */
24777478
JS
5821 int newOffsetY = pos - rect.y;
5822 if (newOffsetY != offsetY)
5823 {
5824 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
5825 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
5826 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
5827 }
cdaed652 5828
24777478 5829 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 5830 x = rect.x;
24777478 5831 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 5832 x = rect.x + rect.width - size.x;
24777478 5833
cdaed652
VZ
5834 anchored->SetPosition(wxPoint(x, pos));
5835 anchored->SetCachedSize(size);
5836 floatCollector->CollectFloat(this, anchored);
5837 }
5838
5839 node = node->GetNext();
5840 }
5841}
5842
603f702b 5843// Get the first position from pos that has a line break character.
ff76711f
JS
5844long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
5845{
5846 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5847 while (node)
5848 {
5849 wxRichTextObject* obj = node->GetData();
5850 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
5851 {
5852 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5853 if (textObj)
5854 {
5855 long breakPos = textObj->GetFirstLineBreakPosition(pos);
5856 if (breakPos > -1)
5857 return breakPos;
5858 }
5859 }
5860 node = node->GetNext();
5861 }
5862 return -1;
5863}
cfa3b256 5864
5d7836c4
JS
5865/*!
5866 * wxRichTextLine
5867 * This object represents a line in a paragraph, and stores
5868 * offsets from the start of the paragraph representing the
5869 * start and end positions of the line.
5870 */
5871
5872wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
5873{
1e967276 5874 Init(parent);
5d7836c4
JS
5875}
5876
5877/// Initialisation
1e967276 5878void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 5879{
1e967276
JS
5880 m_parent = parent;
5881 m_range.SetRange(-1, -1);
5882 m_pos = wxPoint(0, 0);
5883 m_size = wxSize(0, 0);
5d7836c4 5884 m_descent = 0;
2f45f554
JS
5885#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5886 m_objectSizes.Clear();
5887#endif
5d7836c4
JS
5888}
5889
5890/// Copy
5891void wxRichTextLine::Copy(const wxRichTextLine& obj)
5892{
5893 m_range = obj.m_range;
2f45f554
JS
5894#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5895 m_objectSizes = obj.m_objectSizes;
5896#endif
5d7836c4
JS
5897}
5898
5899/// Get the absolute object position
5900wxPoint wxRichTextLine::GetAbsolutePosition() const
5901{
5902 return m_parent->GetPosition() + m_pos;
5903}
5904
1e967276
JS
5905/// Get the absolute range
5906wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
5907{
5908 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
5909 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
5910 return range;
5911}
5912
5d7836c4
JS
5913/*!
5914 * wxRichTextPlainText
5915 * This object represents a single piece of text.
5916 */
5917
5918IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
5919
24777478 5920wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
5921 wxRichTextObject(parent)
5922{
5d7836c4
JS
5923 if (style)
5924 SetAttributes(*style);
5925
5926 m_text = text;
5927}
5928
cfa3b256
JS
5929#define USE_KERNING_FIX 1
5930
4794d69c
JS
5931// If insufficient tabs are defined, this is the tab width used
5932#define WIDTH_FOR_DEFAULT_TABS 50
5933
5d7836c4 5934/// Draw the item
603f702b 5935bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 5936{
fe5aa22c
JS
5937 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
5938 wxASSERT (para != NULL);
5939
603f702b
JS
5940 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
5941
5942 // Let's make the assumption for now that for content in a paragraph, including
5943 // text, we never have a discontinuous selection. So we only deal with a
5944 // single range.
5945 wxRichTextRange selectionRange;
5946 if (selection.IsValid())
5947 {
5948 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
5949 if (selectionRanges.GetCount() > 0)
5950 selectionRange = selectionRanges[0];
5951 else
5952 selectionRange = wxRICHTEXT_NO_SELECTION;
5953 }
5954 else
5955 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 5956
5d7836c4
JS
5957 int offset = GetRange().GetStart();
5958
ff76711f
JS
5959 // Replace line break characters with spaces
5960 wxString str = m_text;
5961 wxString toRemove = wxRichTextLineBreakChar;
5962 str.Replace(toRemove, wxT(" "));
c025e094
JS
5963 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
5964 str.MakeUpper();
3e541562 5965
5d7836c4 5966 long len = range.GetLength();
ff76711f 5967 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 5968
5d7836c4
JS
5969 // Test for the optimized situations where all is selected, or none
5970 // is selected.
5971
30bf7630
JS
5972 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
5973 wxCheckSetFont(dc, textFont);
5974 int charHeight = dc.GetCharHeight();
5975
5976 int x, y;
5977 if ( textFont.Ok() )
5978 {
5979 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
5980 {
5981 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
5982 textFont.SetPointSize( static_cast<int>(size) );
5983 x = rect.x;
5984 y = rect.y;
5985 wxCheckSetFont(dc, textFont);
5986 }
5987 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
5988 {
5989 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
5990 textFont.SetPointSize( static_cast<int>(size) );
5991 x = rect.x;
5992 int sub_height = static_cast<int>( static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
5993 y = rect.y + (rect.height - sub_height + (descent - m_descent));
5994 wxCheckSetFont(dc, textFont);
5995 }
5996 else
5997 {
5998 x = rect.x;
5999 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6000 }
6001 }
6002 else
6003 {
6004 x = rect.x;
6005 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6006 }
5d7836c4 6007
603f702b
JS
6008 // TODO: new selection code
6009
5d7836c4
JS
6010 // (a) All selected.
6011 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6012 {
fe5aa22c 6013 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6014 }
6015 // (b) None selected.
6016 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6017 {
6018 // Draw all unselected
fe5aa22c 6019 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6020 }
6021 else
6022 {
6023 // (c) Part selected, part not
6024 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6025
04ee05f9 6026 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6027
5d7836c4
JS
6028 // 1. Initial unselected chunk, if any, up until start of selection.
6029 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6030 {
6031 int r1 = range.GetStart();
6032 int s1 = selectionRange.GetStart()-1;
6033 int fragmentLen = s1 - r1 + 1;
6034 if (fragmentLen < 0)
af588446 6035 {
5d7836c4 6036 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6037 }
ff76711f 6038 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6039
fe5aa22c 6040 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6041
6042#if USE_KERNING_FIX
6043 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6044 {
6045 // Compensate for kerning difference
ff76711f
JS
6046 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6047 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6048
cfa3b256
JS
6049 wxCoord w1, h1, w2, h2, w3, h3;
6050 dc.GetTextExtent(stringFragment, & w1, & h1);
6051 dc.GetTextExtent(stringFragment2, & w2, & h2);
6052 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6053
cfa3b256
JS
6054 int kerningDiff = (w1 + w3) - w2;
6055 x = x - kerningDiff;
6056 }
6057#endif
5d7836c4
JS
6058 }
6059
6060 // 2. Selected chunk, if any.
6061 if (selectionRange.GetEnd() >= range.GetStart())
6062 {
6063 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6064 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6065
6066 int fragmentLen = s2 - s1 + 1;
6067 if (fragmentLen < 0)
af588446 6068 {
5d7836c4 6069 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6070 }
ff76711f 6071 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6072
fe5aa22c 6073 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6074
6075#if USE_KERNING_FIX
6076 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6077 {
6078 // Compensate for kerning difference
ff76711f
JS
6079 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6080 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6081
cfa3b256
JS
6082 wxCoord w1, h1, w2, h2, w3, h3;
6083 dc.GetTextExtent(stringFragment, & w1, & h1);
6084 dc.GetTextExtent(stringFragment2, & w2, & h2);
6085 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6086
cfa3b256
JS
6087 int kerningDiff = (w1 + w3) - w2;
6088 x = x - kerningDiff;
6089 }
6090#endif
5d7836c4
JS
6091 }
6092
6093 // 3. Remaining unselected chunk, if any
6094 if (selectionRange.GetEnd() < range.GetEnd())
6095 {
6096 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6097 int r2 = range.GetEnd();
6098
6099 int fragmentLen = r2 - s2 + 1;
6100 if (fragmentLen < 0)
af588446 6101 {
5d7836c4 6102 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6103 }
ff76711f 6104 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6105
fe5aa22c 6106 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6107 }
5d7836c4
JS
6108 }
6109
6110 return true;
6111}
61399247 6112
24777478 6113bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6114{
cfa3b256
JS
6115 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6116
6117 wxArrayInt tabArray;
6118 int tabCount;
6119 if (hasTabs)
ab14c7aa 6120 {
cfa3b256
JS
6121 if (attr.GetTabs().IsEmpty())
6122 tabArray = wxRichTextParagraph::GetDefaultTabs();
6123 else
6124 tabArray = attr.GetTabs();
6125 tabCount = tabArray.GetCount();
6126
6127 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6128 {
cfa3b256
JS
6129 int pos = tabArray[i];
6130 pos = ConvertTenthsMMToPixels(dc, pos);
6131 tabArray[i] = pos;
7f0d9d71
JS
6132 }
6133 }
cfa3b256
JS
6134 else
6135 tabCount = 0;
ab14c7aa 6136
cfa3b256
JS
6137 int nextTabPos = -1;
6138 int tabPos = -1;
7f0d9d71 6139 wxCoord w, h;
ab14c7aa 6140
cfa3b256 6141 if (selected)
ab14c7aa 6142 {
0ec6da02
JS
6143 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6144 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6145
ecb5fbf1
JS
6146 wxCheckSetBrush(dc, wxBrush(highlightColour));
6147 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6148 dc.SetTextForeground(highlightTextColour);
04ee05f9 6149 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6150 }
ab14c7aa
JS
6151 else
6152 {
fe5aa22c 6153 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6154
f0e9eda2
JS
6155 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6156 {
04ee05f9 6157 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6158 dc.SetTextBackground(attr.GetBackgroundColour());
6159 }
6160 else
04ee05f9 6161 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6162 }
3e541562 6163
925a662a 6164 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6165 while (hasTabs)
ab14c7aa
JS
6166 {
6167 // the string has a tab
7f0d9d71
JS
6168 // break up the string at the Tab
6169 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6170 str = str.AfterFirst(wxT('\t'));
6171 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6172 tabPos = x + w;
7f0d9d71 6173 bool not_found = true;
cfa3b256 6174 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6175 {
015d0446 6176 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6177
6178 // Find the next tab position.
6179 // Even if we're at the end of the tab array, we must still draw the chunk.
6180
6181 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6182 {
4794d69c
JS
6183 if (nextTabPos <= tabPos)
6184 {
6185 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6186 nextTabPos = tabPos + defaultTabWidth;
6187 }
6188
7f0d9d71 6189 not_found = false;
ab14c7aa
JS
6190 if (selected)
6191 {
cfa3b256 6192 w = nextTabPos - x;
7f0d9d71 6193 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6194 dc.DrawRectangle(selRect);
7f0d9d71
JS
6195 }
6196 dc.DrawText(stringChunk, x, y);
42688aea
JS
6197
6198 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6199 {
6200 wxPen oldPen = dc.GetPen();
ecb5fbf1 6201 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6202 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6203 wxCheckSetPen(dc, oldPen);
42688aea
JS
6204 }
6205
cfa3b256 6206 x = nextTabPos;
7f0d9d71
JS
6207 }
6208 }
cfa3b256 6209 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6210 }
61399247 6211
cfa3b256 6212 if (!str.IsEmpty())
ab14c7aa 6213 {
cfa3b256
JS
6214 dc.GetTextExtent(str, & w, & h);
6215 if (selected)
6216 {
6217 wxRect selRect(x, rect.y, w, rect.GetHeight());
6218 dc.DrawRectangle(selRect);
6219 }
6220 dc.DrawText(str, x, y);
42688aea
JS
6221
6222 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6223 {
6224 wxPen oldPen = dc.GetPen();
ecb5fbf1 6225 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6226 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6227 wxCheckSetPen(dc, oldPen);
42688aea
JS
6228 }
6229
cfa3b256 6230 x += w;
7f0d9d71 6231 }
7f0d9d71 6232 return true;
5d7836c4 6233
7f0d9d71 6234}
fe5aa22c 6235
5d7836c4 6236/// Lay the item out
38113684 6237bool wxRichTextPlainText::Layout(wxDC& dc, const wxRect& WXUNUSED(rect), int WXUNUSED(style))
5d7836c4 6238{
ecb5fbf1
JS
6239 // Only lay out if we haven't already cached the size
6240 if (m_size.x == -1)
6241 GetRangeSize(GetRange(), m_size, m_descent, dc, 0, wxPoint(0, 0));
603f702b
JS
6242 m_maxSize = m_size;
6243 // Eventually we want to have a reasonable estimate of minimum size.
6244 m_minSize = wxSize(0, 0);
5d7836c4
JS
6245 return true;
6246}
6247
6248/// Copy
6249void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6250{
6251 wxRichTextObject::Copy(obj);
6252
6253 m_text = obj.m_text;
6254}
6255
6256/// Get/set the object size for the given range. Returns false if the range
6257/// is invalid for this object.
31778480 6258bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
6259{
6260 if (!range.IsWithin(GetRange()))
6261 return false;
6262
fe5aa22c
JS
6263 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6264 wxASSERT (para != NULL);
603f702b 6265
925a662a 6266 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6267
24777478 6268 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
fe5aa22c 6269
5d7836c4
JS
6270 // Always assume unformatted text, since at this level we have no knowledge
6271 // of line breaks - and we don't need it, since we'll calculate size within
6272 // formatted text by doing it in chunks according to the line ranges
6273
30bf7630 6274 bool bScript(false);
44cc96a8 6275 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
30bf7630
JS
6276 if (font.Ok())
6277 {
6278 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6279 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6280 {
6281 wxFont textFont = font;
6282 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6283 textFont.SetPointSize( static_cast<int>(size) );
6284 wxCheckSetFont(dc, textFont);
6285 bScript = true;
6286 }
6287 else
6288 {
6289 wxCheckSetFont(dc, font);
6290 }
6291 }
5d7836c4 6292
109bfc88 6293 bool haveDescent = false;
5d7836c4
JS
6294 int startPos = range.GetStart() - GetRange().GetStart();
6295 long len = range.GetLength();
3e541562 6296
ff76711f
JS
6297 wxString str(m_text);
6298 wxString toReplace = wxRichTextLineBreakChar;
6299 str.Replace(toReplace, wxT(" "));
6300
6301 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea
JS
6302
6303 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6304 stringChunk.MakeUpper();
6305
5d7836c4 6306 wxCoord w, h;
7f0d9d71 6307 int width = 0;
cfa3b256 6308 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6309 {
6310 // the string has a tab
cfa3b256
JS
6311 wxArrayInt tabArray;
6312 if (textAttr.GetTabs().IsEmpty())
6313 tabArray = wxRichTextParagraph::GetDefaultTabs();
6314 else
6315 tabArray = textAttr.GetTabs();
ab14c7aa 6316
cfa3b256 6317 int tabCount = tabArray.GetCount();
41a85215 6318
cfa3b256 6319 for (int i = 0; i < tabCount; ++i)
61399247 6320 {
cfa3b256
JS
6321 int pos = tabArray[i];
6322 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6323 tabArray[i] = pos;
7f0d9d71 6324 }
41a85215 6325
cfa3b256 6326 int nextTabPos = -1;
61399247 6327
ab14c7aa
JS
6328 while (stringChunk.Find(wxT('\t')) >= 0)
6329 {
109bfc88
JS
6330 int absoluteWidth = 0;
6331
ab14c7aa 6332 // the string has a tab
7f0d9d71
JS
6333 // break up the string at the Tab
6334 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6335 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6336
31778480
JS
6337 if (partialExtents)
6338 {
109bfc88
JS
6339 int oldWidth;
6340 if (partialExtents->GetCount() > 0)
6341 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6342 else
6343 oldWidth = 0;
6344
31778480
JS
6345 // Add these partial extents
6346 wxArrayInt p;
6347 dc.GetPartialTextExtents(stringFragment, p);
6348 size_t j;
6349 for (j = 0; j < p.GetCount(); j++)
6350 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6351
6352 if (partialExtents->GetCount() > 0)
925a662a 6353 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6354 else
925a662a 6355 absoluteWidth = relativeX;
109bfc88
JS
6356 }
6357 else
6358 {
6359 dc.GetTextExtent(stringFragment, & w, & h);
6360 width += w;
603f702b 6361 absoluteWidth = width + relativeX;
109bfc88 6362 haveDescent = true;
31778480
JS
6363 }
6364
cfa3b256
JS
6365 bool notFound = true;
6366 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6367 {
cfa3b256 6368 nextTabPos = tabArray.Item(i);
4794d69c
JS
6369
6370 // Find the next tab position.
6371 // Even if we're at the end of the tab array, we must still process the chunk.
6372
6373 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6374 {
4794d69c
JS
6375 if (nextTabPos <= absoluteWidth)
6376 {
6377 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6378 nextTabPos = absoluteWidth + defaultTabWidth;
6379 }
6380
cfa3b256 6381 notFound = false;
925a662a 6382 width = nextTabPos - relativeX;
31778480
JS
6383
6384 if (partialExtents)
6385 partialExtents->Add(width);
7f0d9d71
JS
6386 }
6387 }
6388 }
6389 }
30bf7630 6390
31778480
JS
6391 if (!stringChunk.IsEmpty())
6392 {
31778480
JS
6393 if (partialExtents)
6394 {
109bfc88
JS
6395 int oldWidth;
6396 if (partialExtents->GetCount() > 0)
6397 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6398 else
6399 oldWidth = 0;
6400
31778480
JS
6401 // Add these partial extents
6402 wxArrayInt p;
6403 dc.GetPartialTextExtents(stringChunk, p);
6404 size_t j;
6405 for (j = 0; j < p.GetCount(); j++)
6406 partialExtents->Add(oldWidth + p[j]);
6407 }
109bfc88
JS
6408 else
6409 {
6410 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6411 width += w;
6412 haveDescent = true;
6413 }
6414 }
6415
6416 if (partialExtents)
6417 {
6418 int charHeight = dc.GetCharHeight();
6419 if ((*partialExtents).GetCount() > 0)
6420 w = (*partialExtents)[partialExtents->GetCount()-1];
6421 else
6422 w = 0;
6423 size = wxSize(w, charHeight);
6424 }
6425 else
6426 {
6427 size = wxSize(width, dc.GetCharHeight());
31778480 6428 }
30bf7630 6429
109bfc88
JS
6430 if (!haveDescent)
6431 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6432
30bf7630
JS
6433 if ( bScript )
6434 dc.SetFont(font);
6435
5d7836c4
JS
6436 return true;
6437}
6438
6439/// Do a split, returning an object containing the second part, and setting
6440/// the first part in 'this'.
6441wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6442{
ff76711f 6443 long index = pos - GetRange().GetStart();
3e541562 6444
28f92d74 6445 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6446 return NULL;
6447
6448 wxString firstPart = m_text.Mid(0, index);
6449 wxString secondPart = m_text.Mid(index);
6450
6451 m_text = firstPart;
6452
6453 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6454 newObject->SetAttributes(GetAttributes());
6455
6456 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6457 GetRange().SetEnd(pos-1);
3e541562 6458
5d7836c4
JS
6459 return newObject;
6460}
6461
6462/// Calculate range
6463void wxRichTextPlainText::CalculateRange(long start, long& end)
6464{
28f92d74 6465 end = start + m_text.length() - 1;
5d7836c4
JS
6466 m_range.SetRange(start, end);
6467}
6468
6469/// Delete range
6470bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6471{
6472 wxRichTextRange r = range;
6473
6474 r.LimitTo(GetRange());
6475
6476 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6477 {
6478 m_text.Empty();
6479 return true;
6480 }
6481
6482 long startIndex = r.GetStart() - GetRange().GetStart();
6483 long len = r.GetLength();
6484
6485 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6486 return true;
6487}
6488
6489/// Get text for the given range.
6490wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6491{
6492 wxRichTextRange r = range;
6493
6494 r.LimitTo(GetRange());
6495
6496 long startIndex = r.GetStart() - GetRange().GetStart();
6497 long len = r.GetLength();
6498
6499 return m_text.Mid(startIndex, len);
6500}
6501
6502/// Returns true if this object can merge itself with the given one.
6503bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
6504{
6505 return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) &&
7fe8059f 6506 (m_text.empty() || wxTextAttrEq(GetAttributes(), object->GetAttributes()));
5d7836c4
JS
6507}
6508
6509/// Returns true if this object merged itself with the given one.
6510/// The calling code will then delete the given object.
6511bool wxRichTextPlainText::Merge(wxRichTextObject* object)
6512{
6513 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
6514 wxASSERT( textObject != NULL );
6515
6516 if (textObject)
6517 {
6518 m_text += textObject->GetText();
99404ab0 6519 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
6520 return true;
6521 }
6522 else
6523 return false;
6524}
6525
6526/// Dump to output stream for debugging
6527void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
6528{
6529 wxRichTextObject::Dump(stream);
6530 stream << m_text << wxT("\n");
6531}
6532
ff76711f
JS
6533/// Get the first position from pos that has a line break character.
6534long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
6535{
6536 int i;
6537 int len = m_text.length();
6538 int startPos = pos - m_range.GetStart();
6539 for (i = startPos; i < len; i++)
6540 {
6541 wxChar ch = m_text[i];
6542 if (ch == wxRichTextLineBreakChar)
6543 {
6544 return i + m_range.GetStart();
6545 }
6546 }
6547 return -1;
6548}
6549
5d7836c4
JS
6550/*!
6551 * wxRichTextBuffer
6552 * This is a kind of box, used to represent the whole buffer
6553 */
6554
6555IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
6556
d2d0adc7
JS
6557wxList wxRichTextBuffer::sm_handlers;
6558wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
6559int wxRichTextBuffer::sm_bulletRightMargin = 20;
6560float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
5d7836c4
JS
6561
6562/// Initialisation
6563void wxRichTextBuffer::Init()
6564{
6565 m_commandProcessor = new wxCommandProcessor;
6566 m_styleSheet = NULL;
6567 m_modified = false;
6568 m_batchedCommandDepth = 0;
6569 m_batchedCommand = NULL;
6570 m_suppressUndo = 0;
d2d0adc7 6571 m_handlerFlags = 0;
44219ff0 6572 m_scale = 1.0;
5d7836c4
JS
6573}
6574
6575/// Initialisation
6576wxRichTextBuffer::~wxRichTextBuffer()
6577{
6578 delete m_commandProcessor;
6579 delete m_batchedCommand;
6580
6581 ClearStyleStack();
d2d0adc7 6582 ClearEventHandlers();
5d7836c4
JS
6583}
6584
85d8909b 6585void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 6586{
85d8909b 6587 Reset();
3e541562 6588
5d7836c4 6589 GetCommandProcessor()->ClearCommands();
5d7836c4 6590
5d7836c4 6591 Modify(false);
1e967276 6592 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
6593}
6594
0ca07313
JS
6595void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
6596{
6597 wxRichTextParagraphLayoutBox::Copy(obj);
6598
6599 m_styleSheet = obj.m_styleSheet;
6600 m_modified = obj.m_modified;
bec80f4f
JS
6601 m_batchedCommandDepth = 0;
6602 if (m_batchedCommand)
6603 delete m_batchedCommand;
6604 m_batchedCommand = NULL;
0ca07313 6605 m_suppressUndo = obj.m_suppressUndo;
603f702b 6606 m_invalidRange = obj.m_invalidRange;
0ca07313
JS
6607}
6608
38f833b1
JS
6609/// Push style sheet to top of stack
6610bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
6611{
6612 if (m_styleSheet)
6613 styleSheet->InsertSheet(m_styleSheet);
6614
6615 SetStyleSheet(styleSheet);
41a85215 6616
38f833b1
JS
6617 return true;
6618}
6619
6620/// Pop style sheet from top of stack
6621wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
6622{
6623 if (m_styleSheet)
6624 {
6625 wxRichTextStyleSheet* oldSheet = m_styleSheet;
6626 m_styleSheet = oldSheet->GetNextSheet();
6627 oldSheet->Unlink();
41a85215 6628
38f833b1
JS
6629 return oldSheet;
6630 }
6631 else
6632 return NULL;
6633}
6634
0ca07313
JS
6635/// Submit command to insert paragraphs
6636bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
6637{
603f702b
JS
6638 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(pos, paragraphs, ctrl, this, flags);
6639}
6640
6641/// Submit command to insert paragraphs
6642bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags)
6643{
6644 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 6645
603f702b 6646 wxRichTextAttr attr(buffer->GetDefaultStyle());
4f32b3cf 6647
24777478
JS
6648 wxRichTextAttr* p = NULL;
6649 wxRichTextAttr paraAttr;
0ca07313
JS
6650 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6651 {
603f702b 6652 paraAttr = GetStyleForNewParagraph(buffer, pos);
0ca07313
JS
6653 if (!paraAttr.IsDefault())
6654 p = & paraAttr;
6655 }
4f32b3cf
JS
6656 else
6657 p = & attr;
0ca07313
JS
6658
6659 action->GetNewParagraphs() = paragraphs;
59509217 6660
71d8e824
JS
6661 if (p && !p->IsDefault())
6662 {
6663 for (wxRichTextObjectList::compatibility_iterator node = action->GetNewParagraphs().GetChildren().GetFirst(); node; node = node->GetNext())
6664 {
6665 wxRichTextObject* child = node->GetData();
6666 child->SetAttributes(*p);
6667 }
6668 }
6669
0ca07313
JS
6670 action->SetPosition(pos);
6671
603f702b 6672 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
6673 if (!paragraphs.GetPartialParagraph())
6674 range.SetEnd(range.GetEnd()+1);
6675
0ca07313 6676 // Set the range we'll need to delete in Undo
99404ab0 6677 action->SetRange(range);
0ca07313 6678
603f702b 6679 buffer->SubmitAction(action);
0ca07313
JS
6680
6681 return true;
6682}
6683
5d7836c4 6684/// Submit command to insert the given text
fe5aa22c 6685bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 6686{
603f702b
JS
6687 return ctrl->GetFocusObject()->InsertTextWithUndo(pos, text, ctrl, this, flags);
6688}
6689
6690/// Submit command to insert the given text
6691bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags)
6692{
6693 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 6694
24777478
JS
6695 wxRichTextAttr* p = NULL;
6696 wxRichTextAttr paraAttr;
fe5aa22c
JS
6697 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6698 {
7c081bd2 6699 // Get appropriate paragraph style
603f702b 6700 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
6701 if (!paraAttr.IsDefault())
6702 p = & paraAttr;
6703 }
6704
fe5aa22c 6705 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 6706
603f702b 6707 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313
JS
6708
6709 if (text.length() > 0 && text.Last() != wxT('\n'))
6710 {
6711 // Don't count the newline when undoing
6712 length --;
5d7836c4 6713 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 6714 }
46ee0e5b
JS
6715 else if (text.length() > 0 && text.Last() == wxT('\n'))
6716 length --;
5d7836c4
JS
6717
6718 action->SetPosition(pos);
6719
6720 // Set the range we'll need to delete in Undo
0ca07313 6721 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 6722
603f702b 6723 buffer->SubmitAction(action);
7fe8059f 6724
5d7836c4
JS
6725 return true;
6726}
6727
6728/// Submit command to insert the given text
fe5aa22c 6729bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 6730{
603f702b
JS
6731 return ctrl->GetFocusObject()->InsertNewlineWithUndo(pos, ctrl, this, flags);
6732}
6733
6734/// Submit command to insert the given text
6735bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags)
6736{
6737 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 6738
24777478
JS
6739 wxRichTextAttr* p = NULL;
6740 wxRichTextAttr paraAttr;
fe5aa22c
JS
6741 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6742 {
603f702b 6743 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
6744 if (!paraAttr.IsDefault())
6745 p = & paraAttr;
6746 }
6747
603f702b 6748 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f
WS
6749
6750 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
6751 action->GetNewParagraphs().AppendChild(newPara);
6752 action->GetNewParagraphs().UpdateRanges();
6753 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
6754 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
6755 long pos1 = pos;
6756
6c0ea513
JS
6757 if (p)
6758 newPara->SetAttributes(*p);
6759
c025e094
JS
6760 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
6761 {
6762 if (para && para->GetRange().GetEnd() == pos)
6763 pos1 ++;
e2d0875a
JS
6764
6765 // Now see if we need to number the paragraph.
6c0ea513 6766 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
6767 {
6768 wxRichTextAttr numberingAttr;
6769 if (FindNextParagraphNumber(para, numberingAttr))
6770 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
6771 }
c025e094
JS
6772 }
6773
5d7836c4
JS
6774 action->SetPosition(pos);
6775
c025e094 6776 // Use the default character style
99404ab0 6777 // Use the default character style
603f702b 6778 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
6779 {
6780 // Check whether the default style merely reflects the paragraph/basic style,
6781 // in which case don't apply it.
603f702b 6782 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
24777478 6783 wxRichTextAttr toApply;
c025e094
JS
6784 if (para)
6785 {
603f702b 6786 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 6787 wxRichTextAttr newAttr;
c025e094
JS
6788 // This filters out attributes that are accounted for by the current
6789 // paragraph/basic style
6790 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
6791 }
6792 else
6793 toApply = defaultStyle;
6794
6795 if (!toApply.IsDefault())
6796 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
6797 }
99404ab0 6798
5d7836c4 6799 // Set the range we'll need to delete in Undo
c025e094 6800 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 6801
603f702b 6802 buffer->SubmitAction(action);
7fe8059f 6803
5d7836c4
JS
6804 return true;
6805}
6806
6807/// Submit command to insert the given image
24777478
JS
6808bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
6809 const wxRichTextAttr& textAttr)
5d7836c4 6810{
603f702b
JS
6811 return ctrl->GetFocusObject()->InsertImageWithUndo(pos, imageBlock, ctrl, this, flags, textAttr);
6812}
6813
6814/// Submit command to insert the given image
6815bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock,
6816 wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags,
6817 const wxRichTextAttr& textAttr)
6818{
6819 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 6820
24777478
JS
6821 wxRichTextAttr* p = NULL;
6822 wxRichTextAttr paraAttr;
fe5aa22c
JS
6823 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6824 {
603f702b 6825 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
6826 if (!paraAttr.IsDefault())
6827 p = & paraAttr;
6828 }
6829
603f702b 6830 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 6831
5d7836c4 6832 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
6833 if (p)
6834 newPara->SetAttributes(*p);
6835
5d7836c4
JS
6836 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
6837 newPara->AppendChild(imageObject);
24777478 6838 imageObject->SetAttributes(textAttr);
5d7836c4
JS
6839 action->GetNewParagraphs().AppendChild(newPara);
6840 action->GetNewParagraphs().UpdateRanges();
6841
6842 action->GetNewParagraphs().SetPartialParagraph(true);
6843
6844 action->SetPosition(pos);
6845
6846 // Set the range we'll need to delete in Undo
6847 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 6848
603f702b 6849 buffer->SubmitAction(action);
7fe8059f 6850
5d7836c4
JS
6851 return true;
6852}
6853
cdaed652 6854// Insert an object with no change of it
603f702b
JS
6855wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
6856{
6857 return ctrl->GetFocusObject()->InsertObjectWithUndo(pos, object, ctrl, this, flags);
6858}
6859
6860// Insert an object with no change of it
6861wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags)
cdaed652 6862{
603f702b 6863 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 6864
24777478
JS
6865 wxRichTextAttr* p = NULL;
6866 wxRichTextAttr paraAttr;
cdaed652
VZ
6867 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6868 {
603f702b 6869 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
6870 if (!paraAttr.IsDefault())
6871 p = & paraAttr;
6872 }
6873
603f702b 6874 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652
VZ
6875
6876 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
6877 if (p)
6878 newPara->SetAttributes(*p);
6879
6880 newPara->AppendChild(object);
6881 action->GetNewParagraphs().AppendChild(newPara);
6882 action->GetNewParagraphs().UpdateRanges();
6883
6884 action->GetNewParagraphs().SetPartialParagraph(true);
6885
6886 action->SetPosition(pos);
6887
6888 // Set the range we'll need to delete in Undo
6889 action->SetRange(wxRichTextRange(pos, pos));
6890
603f702b 6891 buffer->SubmitAction(action);
cdaed652 6892
603f702b
JS
6893 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
6894 return obj;
cdaed652 6895}
603f702b 6896
fe5aa22c
JS
6897/// Get the style that is appropriate for a new paragraph at this position.
6898/// If the previous paragraph has a paragraph style name, look up the next-paragraph
6899/// style.
603f702b 6900wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
6901{
6902 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
6903 if (para)
6904 {
24777478 6905 wxRichTextAttr attr;
d2d0adc7 6906 bool foundAttributes = false;
3e541562 6907
d2d0adc7 6908 // Look for a matching paragraph style
603f702b 6909 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 6910 {
603f702b 6911 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 6912 if (paraDef)
fe5aa22c 6913 {
caad0109
JS
6914 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
6915 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 6916 {
603f702b 6917 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
6918 if (nextParaDef)
6919 {
6920 foundAttributes = true;
603f702b 6921 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
6922 }
6923 }
3e541562 6924
d2d0adc7
JS
6925 // If we didn't find the 'next style', use this style instead.
6926 if (!foundAttributes)
6927 {
6928 foundAttributes = true;
603f702b 6929 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 6930 }
fe5aa22c
JS
6931 }
6932 }
e2d0875a
JS
6933
6934 // Also apply list style if present
603f702b 6935 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 6936 {
603f702b 6937 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
6938 if (listDef)
6939 {
6940 int thisIndent = para->GetAttributes().GetLeftIndent();
6941 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
6942
6943 // Apply the overall list style, and item style for this level
603f702b 6944 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
6945 wxRichTextApplyStyle(attr, listStyle);
6946 attr.SetOutlineLevel(thisLevel);
6947 if (para->GetAttributes().HasBulletNumber())
6948 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
6949 }
34b4899d 6950 }
e2d0875a 6951
d2d0adc7
JS
6952 if (!foundAttributes)
6953 {
6954 attr = para->GetAttributes();
6955 int flags = attr.GetFlags();
fe5aa22c 6956
d2d0adc7
JS
6957 // Eliminate character styles
6958 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
6959 (~ wxTEXT_ATTR_TEXT_COLOUR) |
6960 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
6961 attr.SetFlags(flags);
6962 }
3e541562 6963
fe5aa22c
JS
6964 return attr;
6965 }
6966 else
24777478 6967 return wxRichTextAttr();
fe5aa22c
JS
6968}
6969
5d7836c4 6970/// Submit command to delete this range
12cc29c5 6971bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 6972{
603f702b
JS
6973 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
6974}
6975
6976/// Submit command to delete this range
6977bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
6978{
6979 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 6980
12cc29c5 6981 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
6982
6983 // Set the range to delete
6984 action->SetRange(range);
7fe8059f 6985
5d7836c4
JS
6986 // Copy the fragment that we'll need to restore in Undo
6987 CopyFragment(range, action->GetOldParagraphs());
6988
6c0ea513
JS
6989 // See if we're deleting a paragraph marker, in which case we need to
6990 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
6991 if (range.GetStart() == range.GetEnd())
5d7836c4 6992 {
6c0ea513
JS
6993 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
6994 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 6995 {
6c0ea513
JS
6996 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
6997 if (nextPara && nextPara != para)
5d7836c4 6998 {
6c0ea513
JS
6999 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7000 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7001 }
7002 }
7003 }
7004
603f702b 7005 buffer->SubmitAction(action);
7fe8059f 7006
5d7836c4
JS
7007 return true;
7008}
7009
7010/// Collapse undo/redo commands
7011bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7012{
7013 if (m_batchedCommandDepth == 0)
7014 {
7015 wxASSERT(m_batchedCommand == NULL);
7016 if (m_batchedCommand)
7017 {
0745f364 7018 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7019 }
7020 m_batchedCommand = new wxRichTextCommand(cmdName);
7021 }
7022
7fe8059f 7023 m_batchedCommandDepth ++;
5d7836c4
JS
7024
7025 return true;
7026}
7027
7028/// Collapse undo/redo commands
7029bool wxRichTextBuffer::EndBatchUndo()
7030{
7031 m_batchedCommandDepth --;
7032
7033 wxASSERT(m_batchedCommandDepth >= 0);
7034 wxASSERT(m_batchedCommand != NULL);
7035
7036 if (m_batchedCommandDepth == 0)
7037 {
0745f364 7038 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7039 m_batchedCommand = NULL;
7040 }
7041
7042 return true;
7043}
7044
7045/// Submit immediately, or delay according to whether collapsing is on
7046bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7047{
7048 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7049 {
7050 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7051 cmd->AddAction(action);
7052 cmd->Do();
7053 cmd->GetActions().Clear();
7054 delete cmd;
7055
5d7836c4 7056 m_batchedCommand->AddAction(action);
0745f364 7057 }
5d7836c4
JS
7058 else
7059 {
7060 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7061 cmd->AddAction(action);
7062
7063 // Only store it if we're not suppressing undo.
7064 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7065 }
7066
7067 return true;
7068}
7069
7070/// Begin suppressing undo/redo commands.
7071bool wxRichTextBuffer::BeginSuppressUndo()
7072{
7fe8059f 7073 m_suppressUndo ++;
5d7836c4
JS
7074
7075 return true;
7076}
7077
7078/// End suppressing undo/redo commands.
7079bool wxRichTextBuffer::EndSuppressUndo()
7080{
7fe8059f 7081 m_suppressUndo --;
5d7836c4
JS
7082
7083 return true;
7084}
7085
7086/// Begin using a style
24777478 7087bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7088{
24777478 7089 wxRichTextAttr newStyle(GetDefaultStyle());
5d7836c4
JS
7090
7091 // Save the old default style
24777478 7092 m_attributeStack.Append((wxObject*) new wxRichTextAttr(GetDefaultStyle()));
5d7836c4
JS
7093
7094 wxRichTextApplyStyle(newStyle, style);
7095 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7096
7097 SetDefaultStyle(newStyle);
7098
5d7836c4
JS
7099 return true;
7100}
7101
7102/// End the style
7103bool wxRichTextBuffer::EndStyle()
7104{
63886f6d 7105 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7106 {
7107 wxLogDebug(_("Too many EndStyle calls!"));
7108 return false;
7109 }
7110
09f14108 7111 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7112 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7113 m_attributeStack.Erase(node);
5d7836c4
JS
7114
7115 SetDefaultStyle(*attr);
7116
7117 delete attr;
7118 return true;
7119}
7120
7121/// End all styles
7122bool wxRichTextBuffer::EndAllStyles()
7123{
7124 while (m_attributeStack.GetCount() != 0)
7125 EndStyle();
7126 return true;
7127}
7128
7129/// Clear the style stack
7130void wxRichTextBuffer::ClearStyleStack()
7131{
09f14108 7132 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7133 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7134 m_attributeStack.Clear();
7135}
7136
7137/// Begin using bold
7138bool wxRichTextBuffer::BeginBold()
7139{
24777478 7140 wxRichTextAttr attr;
7d76fbd5 7141 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7142
5d7836c4
JS
7143 return BeginStyle(attr);
7144}
7145
7146/// Begin using italic
7147bool wxRichTextBuffer::BeginItalic()
7148{
24777478 7149 wxRichTextAttr attr;
7d76fbd5 7150 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7151
5d7836c4
JS
7152 return BeginStyle(attr);
7153}
7154
7155/// Begin using underline
7156bool wxRichTextBuffer::BeginUnderline()
7157{
24777478 7158 wxRichTextAttr attr;
44cc96a8 7159 attr.SetFontUnderlined(true);
7fe8059f 7160
5d7836c4
JS
7161 return BeginStyle(attr);
7162}
7163
7164/// Begin using point size
7165bool wxRichTextBuffer::BeginFontSize(int pointSize)
7166{
24777478 7167 wxRichTextAttr attr;
44cc96a8 7168 attr.SetFontSize(pointSize);
7fe8059f 7169
5d7836c4
JS
7170 return BeginStyle(attr);
7171}
7172
7173/// Begin using this font
7174bool wxRichTextBuffer::BeginFont(const wxFont& font)
7175{
24777478 7176 wxRichTextAttr attr;
5d7836c4 7177 attr.SetFont(font);
7fe8059f 7178
5d7836c4
JS
7179 return BeginStyle(attr);
7180}
7181
7182/// Begin using this colour
7183bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7184{
24777478 7185 wxRichTextAttr attr;
5d7836c4
JS
7186 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7187 attr.SetTextColour(colour);
7fe8059f 7188
5d7836c4
JS
7189 return BeginStyle(attr);
7190}
7191
7192/// Begin using alignment
7193bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7194{
24777478 7195 wxRichTextAttr attr;
5d7836c4
JS
7196 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7197 attr.SetAlignment(alignment);
7fe8059f 7198
5d7836c4
JS
7199 return BeginStyle(attr);
7200}
7201
7202/// Begin left indent
7203bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7204{
24777478 7205 wxRichTextAttr attr;
5d7836c4
JS
7206 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7207 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7208
5d7836c4
JS
7209 return BeginStyle(attr);
7210}
7211
7212/// Begin right indent
7213bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7214{
24777478 7215 wxRichTextAttr attr;
5d7836c4
JS
7216 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7217 attr.SetRightIndent(rightIndent);
7fe8059f 7218
5d7836c4
JS
7219 return BeginStyle(attr);
7220}
7221
7222/// Begin paragraph spacing
7223bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7224{
7225 long flags = 0;
7226 if (before != 0)
7227 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7228 if (after != 0)
7229 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7230
24777478 7231 wxRichTextAttr attr;
5d7836c4
JS
7232 attr.SetFlags(flags);
7233 attr.SetParagraphSpacingBefore(before);
7234 attr.SetParagraphSpacingAfter(after);
7fe8059f 7235
5d7836c4
JS
7236 return BeginStyle(attr);
7237}
7238
7239/// Begin line spacing
7240bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
7241{
24777478 7242 wxRichTextAttr attr;
5d7836c4
JS
7243 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
7244 attr.SetLineSpacing(lineSpacing);
7fe8059f 7245
5d7836c4
JS
7246 return BeginStyle(attr);
7247}
7248
7249/// Begin numbered bullet
7250bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
7251{
24777478 7252 wxRichTextAttr attr;
f089713f 7253 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7254 attr.SetBulletStyle(bulletStyle);
7255 attr.SetBulletNumber(bulletNumber);
7256 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7257
5d7836c4
JS
7258 return BeginStyle(attr);
7259}
7260
7261/// Begin symbol bullet
d2d0adc7 7262bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 7263{
24777478 7264 wxRichTextAttr attr;
f089713f 7265 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7266 attr.SetBulletStyle(bulletStyle);
7267 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 7268 attr.SetBulletText(symbol);
7fe8059f 7269
5d7836c4
JS
7270 return BeginStyle(attr);
7271}
7272
f089713f
JS
7273/// Begin standard bullet
7274bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
7275{
24777478 7276 wxRichTextAttr attr;
f089713f
JS
7277 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7278 attr.SetBulletStyle(bulletStyle);
7279 attr.SetLeftIndent(leftIndent, leftSubIndent);
7280 attr.SetBulletName(bulletName);
7281
7282 return BeginStyle(attr);
7283}
7284
5d7836c4
JS
7285/// Begin named character style
7286bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
7287{
7288 if (GetStyleSheet())
7289 {
7290 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7291 if (def)
7292 {
24777478 7293 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7294 return BeginStyle(attr);
7295 }
7296 }
7297 return false;
7298}
7299
7300/// Begin named paragraph style
7301bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
7302{
7303 if (GetStyleSheet())
7304 {
7305 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
7306 if (def)
7307 {
24777478 7308 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7309 return BeginStyle(attr);
7310 }
7311 }
7312 return false;
7313}
7314
f089713f
JS
7315/// Begin named list style
7316bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
7317{
7318 if (GetStyleSheet())
7319 {
7320 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
7321 if (def)
7322 {
24777478 7323 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
7324
7325 attr.SetBulletNumber(number);
7326
7327 return BeginStyle(attr);
7328 }
7329 }
7330 return false;
7331}
7332
d2d0adc7
JS
7333/// Begin URL
7334bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
7335{
24777478 7336 wxRichTextAttr attr;
d2d0adc7
JS
7337
7338 if (!characterStyle.IsEmpty() && GetStyleSheet())
7339 {
7340 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7341 if (def)
7342 {
336d8ae9 7343 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
7344 }
7345 }
7346 attr.SetURL(url);
7347
7348 return BeginStyle(attr);
7349}
7350
5d7836c4
JS
7351/// Adds a handler to the end
7352void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
7353{
7354 sm_handlers.Append(handler);
7355}
7356
7357/// Inserts a handler at the front
7358void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
7359{
7360 sm_handlers.Insert( handler );
7361}
7362
7363/// Removes a handler
7364bool wxRichTextBuffer::RemoveHandler(const wxString& name)
7365{
7366 wxRichTextFileHandler *handler = FindHandler(name);
7367 if (handler)
7368 {
7369 sm_handlers.DeleteObject(handler);
7370 delete handler;
7371 return true;
7372 }
7373 else
7374 return false;
7375}
7376
7377/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
7378wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
7379 wxRichTextFileType imageType)
5d7836c4
JS
7380{
7381 if (imageType != wxRICHTEXT_TYPE_ANY)
7382 return FindHandler(imageType);
0ca07313 7383 else if (!filename.IsEmpty())
5d7836c4
JS
7384 {
7385 wxString path, file, ext;
a51e601e 7386 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
7387 return FindHandler(ext, imageType);
7388 }
0ca07313
JS
7389 else
7390 return NULL;
5d7836c4
JS
7391}
7392
7393
7394/// Finds a handler by name
7395wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
7396{
7397 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7398 while (node)
7399 {
7400 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7401 if (handler->GetName().Lower() == name.Lower()) return handler;
7402
7403 node = node->GetNext();
7404 }
7405 return NULL;
7406}
7407
7408/// Finds a handler by extension and type
d75a69e8 7409wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
7410{
7411 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7412 while (node)
7413 {
7414 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7415 if ( handler->GetExtension().Lower() == extension.Lower() &&
7416 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
7417 return handler;
7418 node = node->GetNext();
7419 }
7420 return 0;
7421}
7422
7423/// Finds a handler by type
d75a69e8 7424wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
7425{
7426 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7427 while (node)
7428 {
7429 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
7430 if (handler->GetType() == type) return handler;
7431 node = node->GetNext();
7432 }
7433 return NULL;
7434}
7435
7436void wxRichTextBuffer::InitStandardHandlers()
7437{
7438 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
7439 AddHandler(new wxRichTextPlainTextHandler);
7440}
7441
7442void wxRichTextBuffer::CleanUpHandlers()
7443{
7444 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7445 while (node)
7446 {
7447 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
7448 wxList::compatibility_iterator next = node->GetNext();
7449 delete handler;
7450 node = next;
7451 }
7452
7453 sm_handlers.Clear();
7454}
7455
1e967276 7456wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 7457{
1e967276
JS
7458 if (types)
7459 types->Clear();
7460
5d7836c4
JS
7461 wxString wildcard;
7462
7463 wxList::compatibility_iterator node = GetHandlers().GetFirst();
7464 int count = 0;
7465 while (node)
7466 {
7467 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 7468 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
7469 {
7470 if (combine)
7471 {
7472 if (count > 0)
7473 wildcard += wxT(";");
7474 wildcard += wxT("*.") + handler->GetExtension();
7475 }
7476 else
7477 {
7478 if (count > 0)
7479 wildcard += wxT("|");
7480 wildcard += handler->GetName();
7481 wildcard += wxT(" ");
7482 wildcard += _("files");
7483 wildcard += wxT(" (*.");
7484 wildcard += handler->GetExtension();
7485 wildcard += wxT(")|*.");
7486 wildcard += handler->GetExtension();
1e967276
JS
7487 if (types)
7488 types->Add(handler->GetType());
5d7836c4
JS
7489 }
7490 count ++;
7491 }
7492
7493 node = node->GetNext();
7494 }
7495
7496 if (combine)
7497 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
7498 return wildcard;
7499}
7500
7501/// Load a file
d75a69e8 7502bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7503{
7504 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7505 if (handler)
1e967276 7506 {
24777478 7507 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7508 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7509 bool success = handler->LoadFile(this, filename);
7510 Invalidate(wxRICHTEXT_ALL);
7511 return success;
7512 }
5d7836c4
JS
7513 else
7514 return false;
7515}
7516
7517/// Save a file
d75a69e8 7518bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7519{
7520 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7521 if (handler)
d2d0adc7
JS
7522 {
7523 handler->SetFlags(GetHandlerFlags());
5d7836c4 7524 return handler->SaveFile(this, filename);
d2d0adc7 7525 }
5d7836c4
JS
7526 else
7527 return false;
7528}
7529
7530/// Load from a stream
d75a69e8 7531bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7532{
7533 wxRichTextFileHandler* handler = FindHandler(type);
7534 if (handler)
1e967276 7535 {
24777478 7536 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7537 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7538 bool success = handler->LoadFile(this, stream);
7539 Invalidate(wxRICHTEXT_ALL);
7540 return success;
7541 }
5d7836c4
JS
7542 else
7543 return false;
7544}
7545
7546/// Save to a stream
d75a69e8 7547bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7548{
7549 wxRichTextFileHandler* handler = FindHandler(type);
7550 if (handler)
d2d0adc7
JS
7551 {
7552 handler->SetFlags(GetHandlerFlags());
5d7836c4 7553 return handler->SaveFile(this, stream);
d2d0adc7 7554 }
5d7836c4
JS
7555 else
7556 return false;
7557}
7558
7559/// Copy the range to the clipboard
7560bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
7561{
7562 bool success = false;
603f702b
JS
7563 wxRichTextParagraphLayoutBox* container = this;
7564 if (GetRichTextCtrl())
7565 container = GetRichTextCtrl()->GetFocusObject();
7566
11ef729d 7567#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 7568
d2142335 7569 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 7570 {
0ca07313
JS
7571 wxTheClipboard->Clear();
7572
7573 // Add composite object
7574
7575 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
7576
7577 {
603f702b 7578 wxString text = container->GetTextForRange(range);
0ca07313
JS
7579
7580#ifdef __WXMSW__
7581 text = wxTextFile::Translate(text, wxTextFileType_Dos);
7582#endif
7583
7584 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
7585 }
7586
7587 // Add rich text buffer data object. This needs the XML handler to be present.
7588
7589 if (FindHandler(wxRICHTEXT_TYPE_XML))
7590 {
7591 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 7592 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
7593
7594 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
7595 }
7596
7597 if (wxTheClipboard->SetData(compositeObject))
7598 success = true;
7599
5d7836c4
JS
7600 wxTheClipboard->Close();
7601 }
0ca07313 7602
39a1c2f2
WS
7603#else
7604 wxUnusedVar(range);
7605#endif
5d7836c4
JS
7606 return success;
7607}
7608
7609/// Paste the clipboard content to the buffer
7610bool wxRichTextBuffer::PasteFromClipboard(long position)
7611{
7612 bool success = false;
603f702b
JS
7613 wxRichTextParagraphLayoutBox* container = this;
7614 if (GetRichTextCtrl())
7615 container = GetRichTextCtrl()->GetFocusObject();
7616
11ef729d 7617#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
7618 if (CanPasteFromClipboard())
7619 {
7620 if (wxTheClipboard->Open())
7621 {
0ca07313
JS
7622 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7623 {
7624 wxRichTextBufferDataObject data;
7625 wxTheClipboard->GetData(data);
7626 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
7627 if (richTextBuffer)
7628 {
603f702b 7629 container->InsertParagraphsWithUndo(position+1, *richTextBuffer, GetRichTextCtrl(), this, 0);
62381daa 7630 if (GetRichTextCtrl())
603f702b 7631 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
7632 delete richTextBuffer;
7633 }
7634 }
f7d83f24 7635 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
7636 #if wxUSE_UNICODE
7637 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
7638 #endif
f7d83f24 7639 )
5d7836c4
JS
7640 {
7641 wxTextDataObject data;
7642 wxTheClipboard->GetData(data);
7643 wxString text(data.GetText());
c21f3211
JS
7644#ifdef __WXMSW__
7645 wxString text2;
7646 text2.Alloc(text.Length()+1);
7647 size_t i;
7648 for (i = 0; i < text.Length(); i++)
7649 {
7650 wxChar ch = text[i];
7651 if (ch != wxT('\r'))
7652 text2 += ch;
7653 }
7654#else
7655 wxString text2 = text;
7656#endif
603f702b 7657 container->InsertTextWithUndo(position+1, text2, GetRichTextCtrl(), this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 7658
62381daa
JS
7659 if (GetRichTextCtrl())
7660 GetRichTextCtrl()->ShowPosition(position + text2.Length());
7661
5d7836c4
JS
7662 success = true;
7663 }
7664 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
7665 {
7666 wxBitmapDataObject data;
7667 wxTheClipboard->GetData(data);
7668 wxBitmap bitmap(data.GetBitmap());
7669 wxImage image(bitmap.ConvertToImage());
7670
603f702b 7671 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 7672
5d7836c4
JS
7673 action->GetNewParagraphs().AddImage(image);
7674
7675 if (action->GetNewParagraphs().GetChildCount() == 1)
7676 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 7677
9c8e10ad 7678 action->SetPosition(position+1);
7fe8059f 7679
5d7836c4 7680 // Set the range we'll need to delete in Undo
9c8e10ad 7681 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 7682
5d7836c4
JS
7683 SubmitAction(action);
7684
7685 success = true;
7686 }
7687 wxTheClipboard->Close();
7688 }
7689 }
39a1c2f2
WS
7690#else
7691 wxUnusedVar(position);
7692#endif
5d7836c4
JS
7693 return success;
7694}
7695
7696/// Can we paste from the clipboard?
7697bool wxRichTextBuffer::CanPasteFromClipboard() const
7698{
7fe8059f 7699 bool canPaste = false;
11ef729d 7700#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 7701 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 7702 {
f7d83f24
VZ
7703 if (wxTheClipboard->IsSupported(wxDF_TEXT)
7704#if wxUSE_UNICODE
603f702b
JS
7705 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
7706#endif
7707 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7708 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 7709 {
7fe8059f 7710 canPaste = true;
5d7836c4
JS
7711 }
7712 wxTheClipboard->Close();
7713 }
39a1c2f2 7714#endif
5d7836c4
JS
7715 return canPaste;
7716}
7717
7718/// Dumps contents of buffer for debugging purposes
7719void wxRichTextBuffer::Dump()
7720{
7721 wxString text;
7722 {
7723 wxStringOutputStream stream(& text);
7724 wxTextOutputStream textStream(stream);
7725 Dump(textStream);
7726 }
7727
7728 wxLogDebug(text);
7729}
7730
d2d0adc7
JS
7731/// Add an event handler
7732bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
7733{
7734 m_eventHandlers.Append(handler);
7735 return true;
7736}
7737
7738/// Remove an event handler
7739bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
7740{
7741 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
7742 if (node)
7743 {
7744 m_eventHandlers.Erase(node);
7745 if (deleteHandler)
7746 delete handler;
3e541562 7747
d2d0adc7
JS
7748 return true;
7749 }
7750 else
7751 return false;
7752}
7753
7754/// Clear event handlers
7755void wxRichTextBuffer::ClearEventHandlers()
7756{
7757 m_eventHandlers.Clear();
7758}
7759
7760/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7761/// otherwise will stop at the first successful one.
7762bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
7763{
7764 bool success = false;
7765 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
7766 {
7767 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
7768 if (handler->ProcessEvent(event))
7769 {
7770 success = true;
7771 if (!sendToAll)
7772 return true;
7773 }
7774 }
7775 return success;
7776}
7777
7778/// Set style sheet and notify of the change
7779bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
7780{
7781 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 7782
d2d0adc7
JS
7783 wxWindowID id = wxID_ANY;
7784 if (GetRichTextCtrl())
7785 id = GetRichTextCtrl()->GetId();
3e541562 7786
d2d0adc7
JS
7787 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, id);
7788 event.SetEventObject(GetRichTextCtrl());
603f702b 7789 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
7790 event.SetOldStyleSheet(oldSheet);
7791 event.SetNewStyleSheet(sheet);
7792 event.Allow();
3e541562 7793
d2d0adc7
JS
7794 if (SendEvent(event) && !event.IsAllowed())
7795 {
7796 if (sheet != oldSheet)
7797 delete sheet;
7798
7799 return false;
7800 }
7801
7802 if (oldSheet && oldSheet != sheet)
7803 delete oldSheet;
7804
7805 SetStyleSheet(sheet);
7806
7807 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
7808 event.SetOldStyleSheet(NULL);
7809 event.Allow();
7810
7811 return SendEvent(event);
7812}
7813
7814/// Set renderer, deleting old one
7815void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
7816{
7817 if (sm_renderer)
7818 delete sm_renderer;
7819 sm_renderer = renderer;
7820}
7821
603f702b
JS
7822/// Hit-testing: returns a flag indicating hit test details, plus
7823/// information about position
7824int wxRichTextBuffer::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
7825{
7826 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, pt, textPosition, obj, contextObj, flags);
7827 if (ret != wxRICHTEXT_HITTEST_NONE)
7828 {
7829 return ret;
7830 }
7831 else
7832 {
7833 textPosition = m_ownRange.GetEnd()-1;
7834 *obj = this;
7835 *contextObj = this;
7836 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
7837 }
7838}
7839
24777478 7840bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7
JS
7841{
7842 if (bulletAttr.GetTextColour().Ok())
7843 {
ecb5fbf1
JS
7844 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
7845 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
7846 }
7847 else
7848 {
ecb5fbf1
JS
7849 wxCheckSetPen(dc, *wxBLACK_PEN);
7850 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
7851 }
7852
7853 wxFont font;
44cc96a8
JS
7854 if (bulletAttr.HasFont())
7855 {
7856 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
7857 }
d2d0adc7
JS
7858 else
7859 font = (*wxNORMAL_FONT);
7860
ecb5fbf1 7861 wxCheckSetFont(dc, font);
d2d0adc7
JS
7862
7863 int charHeight = dc.GetCharHeight();
3e541562 7864
d2d0adc7
JS
7865 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
7866 int bulletHeight = bulletWidth;
7867
7868 int x = rect.x;
3e541562 7869
d2d0adc7
JS
7870 // Calculate the top position of the character (as opposed to the whole line height)
7871 int y = rect.y + (rect.height - charHeight);
3e541562 7872
d2d0adc7
JS
7873 // Calculate where the bullet should be positioned
7874 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 7875
d2d0adc7 7876 // The margin between a bullet and text.
44219ff0 7877 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 7878
d2d0adc7
JS
7879 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
7880 x = rect.x + rect.width - bulletWidth - margin;
7881 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
7882 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 7883
d2d0adc7
JS
7884 if (bulletAttr.GetBulletName() == wxT("standard/square"))
7885 {
7886 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
7887 }
7888 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
7889 {
7890 wxPoint pts[5];
7891 pts[0].x = x; pts[0].y = y + bulletHeight/2;
7892 pts[1].x = x + bulletWidth/2; pts[1].y = y;
7893 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
7894 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 7895
d2d0adc7
JS
7896 dc.DrawPolygon(4, pts);
7897 }
7898 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
7899 {
7900 wxPoint pts[3];
7901 pts[0].x = x; pts[0].y = y;
7902 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
7903 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 7904
d2d0adc7
JS
7905 dc.DrawPolygon(3, pts);
7906 }
603f702b
JS
7907 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
7908 {
7909 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
7910 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
7911 }
d2d0adc7
JS
7912 else // "standard/circle", and catch-all
7913 {
7914 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
7915 }
7916
d2d0adc7
JS
7917 return true;
7918}
7919
24777478 7920bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
7921{
7922 if (!text.empty())
7923 {
7924 wxFont font;
44cc96a8
JS
7925 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
7926 {
24777478 7927 wxRichTextAttr fontAttr;
44cc96a8
JS
7928 fontAttr.SetFontSize(attr.GetFontSize());
7929 fontAttr.SetFontStyle(attr.GetFontStyle());
7930 fontAttr.SetFontWeight(attr.GetFontWeight());
7931 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
7932 fontAttr.SetFontFaceName(attr.GetBulletFont());
7933 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
7934 }
7935 else if (attr.HasFont())
7936 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
7937 else
7938 font = (*wxNORMAL_FONT);
7939
ecb5fbf1 7940 wxCheckSetFont(dc, font);
d2d0adc7
JS
7941
7942 if (attr.GetTextColour().Ok())
7943 dc.SetTextForeground(attr.GetTextColour());
7944
04ee05f9 7945 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
7946
7947 int charHeight = dc.GetCharHeight();
7948 wxCoord tw, th;
7949 dc.GetTextExtent(text, & tw, & th);
7950
7951 int x = rect.x;
7952
7953 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 7954 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
7955
7956 // The margin between a bullet and text.
44219ff0 7957 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 7958
d2d0adc7
JS
7959 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
7960 x = (rect.x + rect.width) - tw - margin;
7961 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
7962 x = x + (rect.width)/2 - tw/2;
7963
7964 dc.DrawText(text, x, y);
3e541562 7965
d2d0adc7
JS
7966 return true;
7967 }
7968 else
7969 return false;
7970}
7971
24777478 7972bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
7973{
7974 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
7975 // with the buffer. The store will allow retrieval from memory, disk or other means.
7976 return false;
7977}
7978
7979/// Enumerate the standard bullet names currently supported
7980bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
7981{
04529b2a 7982 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 7983 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
7984 bulletNames.Add(wxTRANSLATE("standard/square"));
7985 bulletNames.Add(wxTRANSLATE("standard/diamond"));
7986 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
7987
7988 return true;
7989}
5d7836c4 7990
bec80f4f
JS
7991/*!
7992 * wxRichTextBox
7993 */
7994
603f702b 7995IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
7996
7997wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 7998 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
7999{
8000}
8001
8002/// Draw the item
603f702b 8003bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8004{
603f702b
JS
8005 if (!IsShown())
8006 return true;
5ad9ae3a 8007
603f702b
JS
8008 // TODO: if the active object in the control, draw an indication.
8009 // We need to add the concept of active object, and not just focus object,
8010 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8011 // Ultimately we would like to be able to interactively resize an active object
8012 // using drag handles.
8013 return wxRichTextParagraphLayoutBox::Draw(dc, range, selection, rect, descent, style);
8014}
5ad9ae3a 8015
603f702b
JS
8016/// Copy
8017void wxRichTextBox::Copy(const wxRichTextBox& obj)
8018{
8019 wxRichTextParagraphLayoutBox::Copy(obj);
8020}
8021
8022// Edit properties via a GUI
8023bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8024{
8025 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8026 boxDlg.SetAttributes(GetAttributes());
8027
8028 if (boxDlg.ShowModal() == wxID_OK)
8029 {
8030 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8031 // indeterminate in the object.
8032 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8033 return true;
5ad9ae3a 8034 }
603f702b
JS
8035 else
8036 return false;
bec80f4f
JS
8037}
8038
603f702b
JS
8039IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
8040
8041wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
8042 wxRichTextBox(parent)
bec80f4f 8043{
603f702b
JS
8044}
8045
8046/// Draw the item
8047bool wxRichTextCell::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8048{
8049 return wxRichTextBox::Draw(dc, range, selection, rect, descent, style);
8050}
8051
8052/// Copy
8053void wxRichTextCell::Copy(const wxRichTextCell& obj)
8054{
8055 wxRichTextBox::Copy(obj);
8056}
8057
8058// Edit properties via a GUI
8059bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8060{
8061 // We need to gather common attributes for all selected cells.
8062
8063 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
8064 bool multipleCells = false;
8065 wxRichTextAttr attr;
8066
8067 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
8068 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 8069 {
603f702b
JS
8070 wxRichTextAttr clashingAttr, absentAttr;
8071 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8072 size_t i;
8073 int selectedCellCount = 0;
8074 for (i = 0; i < sel.GetCount(); i++)
8075 {
8076 const wxRichTextRange& range = sel[i];
8077 wxRichTextCell* cell = table->GetCell(range.GetStart());
8078 if (cell)
8079 {
8080 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 8081
603f702b
JS
8082 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
8083
8084 selectedCellCount ++;
8085 }
8086 }
8087 multipleCells = selectedCellCount > 1;
5ad9ae3a 8088 }
603f702b
JS
8089 else
8090 {
8091 attr = GetAttributes();
8092 }
8093
8094 wxString caption;
8095 if (multipleCells)
8096 caption = _("Multiple Cell Properties");
8097 else
8098 caption = _("Cell Properties");
8099
8100 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
8101 cellDlg.SetAttributes(attr);
8102
8103 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
8104 if (sizePage)
8105 {
8106 // We don't want position and floating controls for a cell.
8107 sizePage->ShowPositionControls(false);
8108 sizePage->ShowFloatingControls(false);
8109 }
8110
8111 if (cellDlg.ShowModal() == wxID_OK)
8112 {
8113 if (multipleCells)
8114 {
8115 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8116 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8117 // since it may represent clashing attributes across multiple objects.
8118 table->SetCellStyle(sel, attr);
8119 }
8120 else
8121 // For a single object, indeterminate attributes set by the user should be reflected in the
8122 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8123 // the style directly instead of applying (which ignores indeterminate attributes,
8124 // leaving them as they were).
8125 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8126 return true;
8127 }
8128 else
8129 return false;
8130}
8131
8132WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
8133
8134IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
8135
8136wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
8137{
8138 m_rowCount = 0;
8139 m_colCount = 0;
8140}
5ad9ae3a 8141
603f702b
JS
8142// Draws the object.
8143bool wxRichTextTable::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8144{
8145 return wxRichTextBox::Draw(dc, range, selection, rect, descent, style);
bec80f4f
JS
8146}
8147
603f702b
JS
8148WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
8149WX_DEFINE_OBJARRAY(wxRichTextRectArray);
8150
8151// Lays the object out. rect is the space available for layout. Often it will
8152// be the specified overall space for this object, if trying to constrain
8153// layout to a particular size, or it could be the total space available in the
8154// parent. rect is the overall size, so we must subtract margins and padding.
8155// to get the actual available space.
8156bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, int style)
bec80f4f 8157{
603f702b
JS
8158 SetPosition(rect.GetPosition());
8159
8160 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8161 // minimum size if within alloted size, then divide up remaining size
8162 // between rows/cols.
8163
8164 double scale = 1.0;
8165 wxRichTextBuffer* buffer = GetBuffer();
8166 if (buffer) scale = buffer->GetScale();
8167
8168 wxRect availableSpace = GetAvailableContentArea(dc, rect);
8169 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
8170
8171 // If we have no fixed table size, and assuming we're not pushed for
8172 // space, then we don't have to try to stretch the table to fit the contents.
8173 bool stretchToFitTableWidth = false;
8174
8175 int tableWidth = rect.width;
8176 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8177 {
8178 tableWidth = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8179
8180 // Fixed table width, so we do want to stretch columns out if necessary.
8181 stretchToFitTableWidth = true;
8182
8183 // Shouldn't be able to exceed the size passed to this function
8184 tableWidth = wxMin(rect.width, tableWidth);
8185 }
8186
8187 // Get internal padding
8188 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
8189 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8190 paddingLeft = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8191 if (GetAttributes().GetTextBoxAttr().GetPadding().GetRight().IsValid())
8192 paddingRight = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetRight());
8193 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8194 paddingTop = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8195 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8196 paddingBottom = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetBottom());
8197
8198 // Assume that left and top padding are also used for inter-cell padding.
8199 int paddingX = paddingLeft;
8200 int paddingY = paddingTop;
8201
8202 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8203 GetTotalMargin(dc, buffer, GetAttributes(), totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
8204
8205 // Internal table width - the area for content
8206 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
8207
8208 int rowCount = m_cells.GetCount();
8209 if (m_colCount == 0 || rowCount == 0)
8210 {
8211 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
8212 SetCachedSize(overallRect.GetSize());
8213
8214 // Zero content size
8215 SetMinSize(overallRect.GetSize());
8216 SetMaxSize(GetMinSize());
8217 return true;
8218 }
8219
8220 // The final calculated widths
8221 wxArrayInt colWidths(m_colCount);
8222
8223 wxArrayInt absoluteColWidths(m_colCount);
8224 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8225 wxArrayInt percentageColWidths(m_colCount);
8226 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8227 // These are only relevant when the first column contains spanning information.
8228 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8229 wxArrayInt maxColWidths(m_colCount);
8230 wxArrayInt minColWidths(m_colCount);
8231
8232 wxSize tableSize(tableWidth, 0);
8233
8234 int i, j, k;
8235
8236 for (i = 0; i < m_colCount; i++)
8237 {
8238 absoluteColWidths[i] = 0;
8239 // absoluteColWidthsSpanning[i] = 0;
8240 percentageColWidths[i] = -1;
8241 // percentageColWidthsSpanning[i] = -1;
8242 colWidths[i] = 0;
8243 maxColWidths[i] = 0;
8244 minColWidths[i] = 0;
8245 // columnSpans[i] = 1;
8246 }
8247
8248 // (0) Determine which cells are visible according to spans
8249 // 1 2 3 4 5
8250 // __________________
8251 // | | | | | 1
8252 // |------| |----|
8253 // |------| | | 2
8254 // |------| | | 3
8255 // |------------------|
8256 // |__________________| 4
8257
8258 // To calculate cell visibility:
8259 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8260 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8261 // that cell, hide the cell.
8262
8263 // We can also use this array to match the size of spanning cells to the grid. Or just do
8264 // this when we iterate through all cells.
8265
8266 // 0.1: add spanning cells to an array
8267 wxRichTextRectArray rectArray;
8268 for (j = 0; j < m_rowCount; j++)
8269 {
8270 for (i = 0; i < m_colCount; i++)
8271 {
8272 wxRichTextBox* cell = GetCell(j, i);
8273 int colSpan = 1, rowSpan = 1;
8274 if (cell->GetProperties().HasProperty(wxT("colspan")))
8275 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8276 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8277 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8278 if (colSpan > 1 || rowSpan > 1)
8279 {
8280 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
8281 }
8282 }
8283 }
8284 // 0.2: find which cells are subsumed by a spanning cell
8285 for (j = 0; j < m_rowCount; j++)
8286 {
8287 for (i = 0; i < m_colCount; i++)
8288 {
8289 wxRichTextBox* cell = GetCell(j, i);
8290 if (rectArray.GetCount() == 0)
8291 {
8292 cell->Show(true);
8293 }
8294 else
8295 {
8296 int colSpan = 1, rowSpan = 1;
8297 if (cell->GetProperties().HasProperty(wxT("colspan")))
8298 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8299 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8300 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8301 if (colSpan > 1 || rowSpan > 1)
8302 {
8303 // Assume all spanning cells are shown
8304 cell->Show(true);
8305 }
8306 else
8307 {
8308 bool shown = true;
8309 for (k = 0; k < (int) rectArray.GetCount(); k++)
8310 {
8311 if (rectArray[k].Contains(wxPoint(i, j)))
8312 {
8313 shown = false;
8314 break;
8315 }
8316 }
8317 cell->Show(shown);
8318 }
8319 }
8320 }
8321 }
8322
8323 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8324 // overlap with a spanned cell starting at a previous column position.
8325 // This means we need to keep an array of rects so we can check. However
8326 // it does also mean that some spans simply may not be taken into account
8327 // where there are different spans happening on different rows. In these cases,
8328 // they will simply be as wide as their constituent columns.
8329
8330 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8331 // the absolute or percentage width of each column.
8332
8333 for (j = 0; j < m_rowCount; j++)
8334 {
8335 // First get the overall margins so we can calculate percentage widths based on
8336 // the available content space for all cells on the row
8337
8338 int overallRowContentMargin = 0;
8339 int visibleCellCount = 0;
8340
8341 for (i = 0; i < m_colCount; i++)
8342 {
8343 wxRichTextBox* cell = GetCell(j, i);
8344 if (cell->IsShown())
8345 {
8346 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
8347 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
8348
8349 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
8350 visibleCellCount ++;
8351 }
8352 }
8353
8354 // Add in inter-cell padding
8355 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
8356
8357 int rowContentWidth = internalTableWidth - overallRowContentMargin;
8358 wxSize rowTableSize(rowContentWidth, 0);
8359 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
8360
8361 for (i = 0; i < m_colCount; i++)
8362 {
8363 wxRichTextBox* cell = GetCell(j, i);
8364 if (cell->IsShown())
8365 {
8366 int colSpan = 1;
8367 if (cell->GetProperties().HasProperty(wxT("colspan")))
8368 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8369
8370 // Lay out cell to find min/max widths
8371 cell->Invalidate(wxRICHTEXT_ALL);
8372 cell->Layout(dc, availableSpace, style);
8373
8374 if (colSpan == 1)
8375 {
8376 int absoluteCellWidth = -1;
8377 int percentageCellWidth = -1;
8378
8379 // I think we need to calculate percentages from the internal table size,
8380 // minus the padding between cells which we'll need to calculate from the
8381 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8382 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8383 // so if we want to conform to that we'll need to add in the overall cell margins.
8384 // However, this will make it difficult to specify percentages that add up to
8385 // 100% and still fit within the table width.
8386 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8387 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8388 // If we're using internal content size for the width, we would calculate the
8389 // the overall cell width for n cells as:
8390 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8391 // + thisOverallCellMargin
8392 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8393 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8394
8395 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8396 {
8397 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
8398 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
8399 {
8400 percentageCellWidth = w;
8401 }
8402 else
8403 {
8404 absoluteCellWidth = w;
8405 }
8406 // Override absolute width with minimum width if necessary
8407 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
8408 absoluteCellWidth = cell->GetMinSize().x;
8409 }
8410
8411 if (absoluteCellWidth != -1)
8412 {
8413 if (absoluteCellWidth > absoluteColWidths[i])
8414 absoluteColWidths[i] = absoluteCellWidth;
8415 }
8416
8417 if (percentageCellWidth != -1)
8418 {
8419 if (percentageCellWidth > percentageColWidths[i])
8420 percentageColWidths[i] = percentageCellWidth;
8421 }
8422
8423 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
8424 minColWidths[i] = cell->GetMinSize().x;
8425 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
8426 maxColWidths[i] = cell->GetMaxSize().x;
8427 }
8428 }
8429 }
8430 }
8431
8432 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8433 // TODO: simply merge this into (1).
8434 for (i = 0; i < m_colCount; i++)
8435 {
8436 if (absoluteColWidths[i] > 0)
8437 {
8438 colWidths[i] = absoluteColWidths[i];
8439 }
8440 else if (percentageColWidths[i] > 0)
8441 {
8442 colWidths[i] = percentageColWidths[i];
8443
8444 // This is rubbish - we calculated the absolute widths from percentages, so
8445 // we can't do it again here.
8446 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8447 }
8448 }
8449
8450 // (3) Process absolute or proportional widths of spanning columns,
8451 // now that we know what our fixed column widths are going to be.
8452 // Spanned cells will try to adjust columns so the span will fit.
8453 // Even existing fixed column widths can be expanded if necessary.
8454 // Actually, currently fixed columns widths aren't adjusted; instead,
8455 // the algorithm favours earlier rows and adjusts unspecified column widths
8456 // the first time only. After that, we can't know whether the column has been
8457 // specified explicitly or not. (We could make a note if necessary.)
8458 for (j = 0; j < m_rowCount; j++)
8459 {
8460 // First get the overall margins so we can calculate percentage widths based on
8461 // the available content space for all cells on the row
8462
8463 int overallRowContentMargin = 0;
8464 int visibleCellCount = 0;
8465
8466 for (i = 0; i < m_colCount; i++)
8467 {
8468 wxRichTextBox* cell = GetCell(j, i);
8469 if (cell->IsShown())
8470 {
8471 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
8472 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
8473
8474 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
8475 visibleCellCount ++;
8476 }
8477 }
8478
8479 // Add in inter-cell padding
8480 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
8481
8482 int rowContentWidth = internalTableWidth - overallRowContentMargin;
8483 wxSize rowTableSize(rowContentWidth, 0);
8484 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
8485
8486 for (i = 0; i < m_colCount; i++)
8487 {
8488 wxRichTextBox* cell = GetCell(j, i);
8489 if (cell->IsShown())
8490 {
8491 int colSpan = 1;
8492 if (cell->GetProperties().HasProperty(wxT("colspan")))
8493 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8494
8495 if (colSpan > 1)
8496 {
8497 int spans = wxMin(colSpan, m_colCount - i);
8498 int cellWidth = 0;
8499 if (spans > 0)
8500 {
8501 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8502 {
8503 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
8504 // Override absolute width with minimum width if necessary
8505 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
8506 cellWidth = cell->GetMinSize().x;
8507 }
8508 else
8509 {
8510 // Do we want to do this? It's the only chance we get to
8511 // use the cell's min/max sizes, so we need to work out
8512 // how we're going to balance the unspecified spanning cell
8513 // width with the possibility more-constrained constituent cell widths.
8514 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8515 // don't want to constraint all the spanned columns to fit into this cell.
8516 // OK, let's say that if any of the constituent columns don't fit,
8517 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8518 // cells to the columns later.
8519 cellWidth = cell->GetMinSize().x;
8520 if (cell->GetMaxSize().x > cellWidth)
8521 cellWidth = cell->GetMaxSize().x;
8522 }
8523
8524 // Subtract the padding between cells
8525 int spanningWidth = cellWidth;
8526 spanningWidth -= paddingX * (spans-1);
8527
8528 if (spanningWidth > 0)
8529 {
8530 // Now share the spanning width between columns within that span
8531 // TODO: take into account min widths of columns within the span
8532 int spanningWidthLeft = spanningWidth;
8533 int stretchColCount = 0;
8534 for (k = i; k < (i+spans); k++)
8535 {
8536 if (colWidths[k] > 0) // absolute or proportional width has been specified
8537 spanningWidthLeft -= colWidths[k];
8538 else
8539 stretchColCount ++;
8540 }
8541 // Now divide what's left between the remaining columns
8542 int colShare = 0;
8543 if (stretchColCount > 0)
8544 colShare = spanningWidthLeft / stretchColCount;
8545 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
8546
8547 // If fixed-width columns are currently too big, then we'll later
8548 // stretch the spanned cell to fit.
8549
8550 if (spanningWidthLeft > 0)
8551 {
8552 for (k = i; k < (i+spans); k++)
8553 {
8554 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
8555 {
8556 int newWidth = colShare;
8557 if (k == (i+spans-1))
8558 newWidth += colShareRemainder; // ensure all pixels are filled
8559 colWidths[k] = newWidth;
8560 }
8561 }
8562 }
8563 }
8564 }
8565 }
8566 }
8567 }
8568 }
8569
8570 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8571 // TODO: take into account min widths of columns within the span
8572 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
8573 int widthLeft = tableWidthMinusPadding;
8574 int stretchColCount = 0;
8575 for (i = 0; i < m_colCount; i++)
8576 {
8577 // TODO: we need to take into account min widths.
8578 // Subtract min width from width left, then
8579 // add the colShare to the min width
8580 if (colWidths[i] > 0) // absolute or proportional width has been specified
8581 widthLeft -= colWidths[i];
8582 else
8583 {
8584 if (minColWidths[i] > 0)
8585 widthLeft -= minColWidths[i];
8586
8587 stretchColCount ++;
8588 }
8589 }
8590
8591 // Now divide what's left between the remaining columns
8592 int colShare = 0;
8593 if (stretchColCount > 0)
8594 colShare = widthLeft / stretchColCount;
8595 int colShareRemainder = widthLeft - (colShare * stretchColCount);
8596
8597 // Check we don't have enough space, in which case shrink all columns, overriding
8598 // any absolute/proportional widths
8599 // TODO: actually we would like to divide up the shrinkage according to size.
8600 // How do we calculate the proportions that will achieve this?
8601 // Could first choose an arbitrary value for stretching cells, and then calculate
8602 // factors to multiply each width by.
8603 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8604 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
8605 {
8606 colShare = tableWidthMinusPadding / m_colCount;
8607 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
8608 for (i = 0; i < m_colCount; i++)
8609 {
8610 colWidths[i] = 0;
8611 minColWidths[i] = 0;
8612 }
8613 }
8614
8615 // We have to adjust the columns if either we need to shrink the
8616 // table to fit the parent/table width, or we explicitly set the
8617 // table width and need to stretch out the table.
8618 if (widthLeft < 0 || stretchToFitTableWidth)
8619 {
8620 for (i = 0; i < m_colCount; i++)
8621 {
8622 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
8623 {
8624 if (minColWidths[i] > 0)
8625 colWidths[i] = minColWidths[i] + colShare;
8626 else
8627 colWidths[i] = colShare;
8628 if (i == (m_colCount-1))
8629 colWidths[i] += colShareRemainder; // ensure all pixels are filled
8630 }
8631 }
8632 }
8633
8634 // TODO: if spanned cells have no specified or max width, make them the
8635 // as big as the columns they span. Do this for all spanned cells in all
8636 // rows, of course. Size any spanned cells left over at the end - even if they
8637 // have width > 0, make sure they're limited to the appropriate column edge.
8638
8639
8640/*
8641 Sort out confusion between content width
8642 and overall width later. For now, assume we specify overall width.
8643
8644 So, now we've laid out the table to fit into the given space
8645 and have used specified widths and minimum widths.
8646
8647 Now we need to consider how we will try to take maximum width into account.
8648
8649*/
8650
8651 // (??) TODO: take max width into account
8652
8653 // (6) Lay out all cells again with the current values
8654
8655 int maxRight = 0;
8656 int y = availableSpace.y;
8657 for (j = 0; j < m_rowCount; j++)
8658 {
8659 int x = availableSpace.x; // TODO: take into account centering etc.
8660 int maxCellHeight = 0;
8661 int maxSpecifiedCellHeight = 0;
8662
8663 wxArrayInt actualWidths(m_colCount);
8664
8665 wxTextAttrDimensionConverter converter(dc, scale);
8666 for (i = 0; i < m_colCount; i++)
8667 {
8668 wxRichTextCell* cell = GetCell(j, i);
8669 if (cell->IsShown())
8670 {
8671 wxASSERT(colWidths[i] > 0);
8672
8673 // Get max specified cell height
8674 // Don't handle percentages for height
8675 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
8676 {
8677 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
8678 if (h > maxSpecifiedCellHeight)
8679 maxSpecifiedCellHeight = h;
8680 }
8681
8682 if (colWidths[i] > 0) // absolute or proportional width has been specified
8683 {
8684 int colSpan = 1;
8685 if (cell->GetProperties().HasProperty(wxT("colspan")))
8686 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8687
8688 wxRect availableCellSpace;
8689
8690 // TODO: take into acount spans
8691 if (colSpan > 1)
8692 {
8693 // Calculate the size of this spanning cell from its constituent columns
8694 int xx = x;
8695 int spans = wxMin(colSpan, m_colCount - i);
8696 for (k = i; k < spans; k++)
8697 {
8698 if (k != i)
8699 xx += paddingX;
8700 xx += colWidths[k];
8701 }
8702 availableCellSpace = wxRect(x, y, xx, -1);
8703 }
8704 else
8705 availableCellSpace = wxRect(x, y, colWidths[i], -1);
8706
8707 // Store actual width so we can force cell to be the appropriate width on the final loop
8708 actualWidths[i] = availableCellSpace.GetWidth();
8709
8710 // Lay out cell
8711 cell->Invalidate(wxRICHTEXT_ALL);
8712 cell->Layout(dc, availableCellSpace, style);
8713
8714 // TODO: use GetCachedSize().x to compute 'natural' size
8715
8716 x += (availableCellSpace.GetWidth() + paddingX);
8717 if (cell->GetCachedSize().y > maxCellHeight)
8718 maxCellHeight = cell->GetCachedSize().y;
8719 }
8720 }
8721 }
8722
8723 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
8724
8725 for (i = 0; i < m_colCount; i++)
8726 {
8727 wxRichTextCell* cell = GetCell(j, i);
8728 if (cell->IsShown())
8729 {
8730 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
8731 // Lay out cell with new height
8732 cell->Invalidate(wxRICHTEXT_ALL);
8733 cell->Layout(dc, availableCellSpace, style);
8734
8735 // Make sure the cell size really is the appropriate size,
8736 // not the calculated box size
8737 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
8738
8739 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
8740 }
8741 }
8742
8743 y += maxCellHeight;
8744 if (j < (m_rowCount-1))
8745 y += paddingY;
8746 }
8747
8748 // We need to add back the margins etc.
8749 {
8750 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
8751 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8752 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
8753 SetCachedSize(marginRect.GetSize());
8754 }
8755
8756 // TODO: calculate max size
8757 {
8758 SetMaxSize(GetCachedSize());
8759 }
8760
8761 // TODO: calculate min size
8762 {
8763 SetMinSize(GetCachedSize());
8764 }
8765
8766 // TODO: currently we use either a fixed table width or the parent's size.
8767 // We also want to be able to calculate the table width from its content,
8768 // whether using fixed column widths or cell content min/max width.
8769 // Probably need a boolean flag to say whether we need to stretch cells
8770 // to fit the table width, or to simply use min/max cell widths. The
8771 // trouble with this is that if cell widths are not specified, they
8772 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8773 // Anyway, ignoring that problem, we probably need to factor layout into a function
8774 // that can can calculate the maximum unconstrained layout in case table size is
8775 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8776 // constrain Layout(), or the previously-calculated max size to constraint layout.
8777
8778 return true;
8779}
8780
8781// Finds the absolute position and row height for the given character position
8782bool wxRichTextTable::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
8783{
8784 wxRichTextCell* child = GetCell(index+1);
8785 if (child)
8786 {
8787 // Find the position at the start of the child cell, since the table doesn't
8788 // have any caret position of its own.
8789 return child->FindPosition(dc, -1, pt, height, forceLineStart);
8790 }
8791 else
8792 return false;
8793}
8794
8795// Get the cell at the given character position (in the range of the table).
8796wxRichTextCell* wxRichTextTable::GetCell(long pos) const
8797{
8798 int row = 0, col = 0;
8799 if (GetCellRowColumnPosition(pos, row, col))
8800 {
8801 return GetCell(row, col);
8802 }
8803 else
8804 return NULL;
8805}
8806
8807// Get the row/column for a given character position
8808bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
8809{
8810 if (m_colCount == 0 || m_rowCount == 0)
8811 return false;
8812
8813 row = (int) (pos / m_colCount);
8814 col = pos - (row * m_colCount);
8815
8816 wxASSERT(row < m_rowCount && col < m_colCount);
8817
8818 if (row < m_rowCount && col < m_colCount)
8819 return true;
8820 else
8821 return false;
8822}
8823
8824// Calculate range, taking row/cell ordering into account instead of relying
8825// on list ordering.
8826void wxRichTextTable::CalculateRange(long start, long& end)
8827{
8828 long current = start;
8829 long lastEnd = current;
8830
8831 if (IsTopLevel())
8832 {
8833 current = 0;
8834 lastEnd = 0;
8835 }
8836
8837 int i, j;
8838 for (i = 0; i < m_rowCount; i++)
8839 {
8840 for (j = 0; j < m_colCount; j++)
8841 {
8842 wxRichTextCell* child = GetCell(i, j);
8843 if (child)
8844 {
8845 long childEnd = 0;
8846
8847 child->CalculateRange(current, childEnd);
8848
8849 lastEnd = childEnd;
8850 current = childEnd + 1;
8851 }
8852 }
8853 }
8854
8855 // A top-level object always has a range of size 1,
8856 // because its children don't count at this level.
8857 end = start;
8858 m_range.SetRange(start, start);
8859
8860 // An object with no children has zero length
8861 if (m_children.GetCount() == 0)
8862 lastEnd --;
8863 m_ownRange.SetRange(0, lastEnd);
8864}
8865
8866// Gets the range size.
8867bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
8868{
8869 return wxRichTextBox::GetRangeSize(range, size, descent, dc, flags, position, partialExtents);
8870}
8871
8872// Deletes content in the given range.
8873bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
8874{
8875 // TODO: implement deletion of cells
8876 return true;
8877}
8878
8879// Gets any text in this object for the given range.
8880wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
8881{
8882 return wxRichTextBox::GetTextForRange(range);
8883}
8884
8885// Copies this object.
8886void wxRichTextTable::Copy(const wxRichTextTable& obj)
8887{
8888 wxRichTextBox::Copy(obj);
8889
8890 ClearTable();
8891
8892 m_rowCount = obj.m_rowCount;
8893 m_colCount = obj.m_colCount;
8894
8895 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
8896
8897 int i, j;
8898 for (i = 0; i < m_rowCount; i++)
8899 {
8900 wxRichTextObjectPtrArray& colArray = m_cells[i];
8901 for (j = 0; j < m_colCount; j++)
8902 {
8903 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
8904 AppendChild(cell);
8905
8906 colArray.Add(cell);
8907 }
8908 }
8909}
8910
8911void wxRichTextTable::ClearTable()
8912{
8913 m_cells.Clear();
8914 DeleteChildren();
8915}
8916
8917bool wxRichTextTable::CreateTable(int rows, int cols)
8918{
8919 ClearTable();
8920
8921 m_rowCount = rows;
8922 m_colCount = cols;
8923
8924 m_cells.Add(wxRichTextObjectPtrArray(), rows);
8925
8926 int i, j;
8927 for (i = 0; i < rows; i++)
8928 {
8929 wxRichTextObjectPtrArray& colArray = m_cells[i];
8930 for (j = 0; j < cols; j++)
8931 {
8932 wxRichTextCell* cell = new wxRichTextCell;
8933 AppendChild(cell);
8934 cell->AddParagraph(wxEmptyString);
8935
8936 colArray.Add(cell);
8937 }
8938 }
8939
8940 return true;
8941}
8942
8943wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
8944{
8945 wxASSERT(row < m_rowCount);
8946 wxASSERT(col < m_colCount);
8947
8948 if (row < m_rowCount && col < m_colCount)
8949 {
8950 wxRichTextObjectPtrArray& colArray = m_cells[row];
8951 wxRichTextObject* obj = colArray[col];
8952 return wxDynamicCast(obj, wxRichTextCell);
8953 }
8954 else
8955 return false;
8956}
8957
8958// Returns a selection object specifying the selections between start and end character positions.
8959// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
8960wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
8961{
8962 wxRichTextSelection selection;
8963 selection.SetContainer((wxRichTextTable*) this);
8964
8965 if (start > end)
8966 {
8967 long tmp = end;
8968 end = start;
8969 start = tmp;
8970 }
8971
8972 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
8973
8974 if (end >= (m_colCount * m_rowCount))
8975 return selection;
8976
8977 // We need to find the rectangle of cells that is described by the rectangle
8978 // with start, end as the diagonal. Make sure we don't add cells that are
8979 // not currenty visible because they are overlapped by spanning cells.
8980/*
8981 --------------------------
8982 | 0 | 1 | 2 | 3 | 4 |
8983 --------------------------
8984 | 5 | 6 | 7 | 8 | 9 |
8985 --------------------------
8986 | 10 | 11 | 12 | 13 | 14 |
8987 --------------------------
8988 | 15 | 16 | 17 | 18 | 19 |
8989 --------------------------
8990
8991 Let's say we select 6 -> 18.
8992
8993 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
8994 which is left and which is right.
8995
8996 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
8997
8998 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
8999 and (b) shown.
9000
9001
9002*/
9003
9004 int leftCol = start - m_colCount * int(start/m_colCount);
9005 int rightCol = end - m_colCount * int(end/m_colCount);
9006
9007 int topRow = int(start/m_colCount);
9008 int bottomRow = int(end/m_colCount);
9009
9010 if (leftCol > rightCol)
9011 {
9012 int tmp = rightCol;
9013 rightCol = leftCol;
9014 leftCol = tmp;
9015 }
9016
9017 if (topRow > bottomRow)
9018 {
9019 int tmp = bottomRow;
9020 bottomRow = topRow;
9021 topRow = tmp;
9022 }
9023
9024 int i, j;
9025 for (i = topRow; i <= bottomRow; i++)
9026 {
9027 for (j = leftCol; j <= rightCol; j++)
9028 {
9029 wxRichTextCell* cell = GetCell(i, j);
9030 if (cell && cell->IsShown())
9031 selection.Add(cell->GetRange());
9032 }
9033 }
9034
9035 return selection;
9036}
9037
9038// Sets the attributes for the cells specified by the selection.
9039bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
9040{
9041 if (selection.GetContainer() != this)
9042 return false;
9043
9044 wxRichTextBuffer* buffer = GetBuffer();
9045 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
9046 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
9047
9048 if (withUndo)
9049 buffer->BeginBatchUndo(_("Set Cell Style"));
9050
9051 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
9052 while (node)
9053 {
9054 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
9055 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
9056 SetStyle(cell, style, flags);
9057 node = node->GetNext();
9058 }
9059
9060 // Do action, or delay it until end of batch.
9061 if (withUndo)
9062 buffer->EndBatchUndo();
9063
9064 return true;
9065}
9066
9067bool wxRichTextTable::DeleteRows(int startRow, int noRows)
9068{
9069 wxASSERT((startRow + noRows) < m_rowCount);
9070 if ((startRow + noRows) >= m_rowCount)
9071 return false;
9072
9073 int i, j;
9074 for (i = startRow; i < (startRow+noRows); i++)
9075 {
9076 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
9077 for (j = 0; j < (int) colArray.GetCount(); j++)
9078 {
9079 wxRichTextObject* cell = colArray[j];
9080 RemoveChild(cell, true);
9081 }
9082
9083 // Keep deleting at the same position, since we move all
9084 // the others up
9085 m_cells.RemoveAt(startRow);
9086 }
9087
9088 m_rowCount = m_rowCount - noRows;
9089
9090 return true;
9091}
9092
9093bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
9094{
9095 wxASSERT((startCol + noCols) < m_colCount);
9096 if ((startCol + noCols) >= m_colCount)
9097 return false;
9098
9099 bool deleteRows = (noCols == m_colCount);
9100
9101 int i, j;
9102 for (i = 0; i < m_rowCount; i++)
9103 {
9104 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
9105 for (j = startCol; j < (startCol+noCols); j++)
9106 {
9107 wxRichTextObject* cell = colArray[j];
9108 RemoveChild(cell, true);
9109 }
9110
9111 if (deleteRows)
9112 m_cells.RemoveAt(0);
9113 }
9114
9115 if (deleteRows)
9116 m_rowCount = 0;
9117 m_colCount = m_colCount - noCols;
9118
9119 return true;
9120}
9121
9122bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
9123{
9124 wxASSERT(startRow <= m_rowCount);
9125 if (startRow > m_rowCount)
9126 return false;
9127
9128 int i, j;
9129 for (i = 0; i < noRows; i++)
9130 {
9131 int idx;
9132 if (startRow == m_rowCount)
9133 {
9134 m_cells.Add(wxRichTextObjectPtrArray());
9135 idx = m_cells.GetCount() - 1;
9136 }
9137 else
9138 {
9139 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
9140 idx = startRow+i;
9141 }
9142
9143 wxRichTextObjectPtrArray& colArray = m_cells[idx];
9144 for (j = 0; j < m_colCount; j++)
9145 {
9146 wxRichTextCell* cell = new wxRichTextCell;
9147 cell->GetAttributes() = attr;
9148
9149 AppendChild(cell);
9150 colArray.Add(cell);
9151 }
9152 }
9153
9154 m_rowCount = m_rowCount + noRows;
9155 return true;
9156}
9157
9158bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
9159{
9160 wxASSERT(startCol <= m_colCount);
9161 if (startCol > m_colCount)
9162 return false;
9163
9164 int i, j;
9165 for (i = 0; i < m_rowCount; i++)
9166 {
9167 wxRichTextObjectPtrArray& colArray = m_cells[i];
9168 for (j = 0; j < noCols; j++)
9169 {
9170 wxRichTextCell* cell = new wxRichTextCell;
9171 cell->GetAttributes() = attr;
9172
9173 AppendChild(cell);
9174
9175 if (startCol == m_colCount)
9176 colArray.Add(cell);
9177 else
9178 colArray.Insert(cell, startCol+j);
9179 }
9180 }
9181
9182 m_colCount = m_colCount + noCols;
9183
9184 return true;
5ad9ae3a
JS
9185}
9186
603f702b
JS
9187// Edit properties via a GUI
9188bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 9189{
603f702b
JS
9190 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
9191 boxDlg.SetAttributes(GetAttributes());
9192
9193 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 9194 {
603f702b
JS
9195 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9196 return true;
5ad9ae3a
JS
9197 }
9198 else
9199 return false;
bec80f4f
JS
9200}
9201
5d7836c4
JS
9202/*
9203 * Module to initialise and clean up handlers
9204 */
9205
9206class wxRichTextModule: public wxModule
9207{
9208DECLARE_DYNAMIC_CLASS(wxRichTextModule)
9209public:
9210 wxRichTextModule() {}
cfa3b256
JS
9211 bool OnInit()
9212 {
d2d0adc7 9213 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
9214 wxRichTextBuffer::InitStandardHandlers();
9215 wxRichTextParagraph::InitDefaultTabs();
9216 return true;
47b378bd 9217 }
cfa3b256
JS
9218 void OnExit()
9219 {
9220 wxRichTextBuffer::CleanUpHandlers();
9221 wxRichTextDecimalToRoman(-1);
9222 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 9223 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 9224 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 9225 }
5d7836c4
JS
9226};
9227
9228IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
9229
9230
f1d6804f
RD
9231// If the richtext lib is dynamically loaded after the app has already started
9232// (such as from wxPython) then the built-in module system will not init this
9233// module. Provide this function to do it manually.
9234void wxRichTextModuleInit()
9235{
9236 wxModule* module = new wxRichTextModule;
9237 module->Init();
9238 wxModule::RegisterModule(module);
9239}
9240
9241
5d7836c4
JS
9242/*!
9243 * Commands for undo/redo
9244 *
9245 */
9246
9247wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 9248 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 9249{
603f702b 9250 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
9251}
9252
7fe8059f 9253wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
9254{
9255}
9256
9257wxRichTextCommand::~wxRichTextCommand()
9258{
9259 ClearActions();
9260}
9261
9262void wxRichTextCommand::AddAction(wxRichTextAction* action)
9263{
9264 if (!m_actions.Member(action))
9265 m_actions.Append(action);
9266}
9267
9268bool wxRichTextCommand::Do()
9269{
09f14108 9270 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
9271 {
9272 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
9273 action->Do();
9274 }
9275
9276 return true;
9277}
9278
9279bool wxRichTextCommand::Undo()
9280{
09f14108 9281 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
9282 {
9283 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
9284 action->Undo();
9285 }
9286
9287 return true;
9288}
9289
9290void wxRichTextCommand::ClearActions()
9291{
9292 WX_CLEAR_LIST(wxList, m_actions);
9293}
9294
9295/*!
9296 * Individual action
9297 *
9298 */
9299
603f702b
JS
9300wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
9301 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
9302 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
9303{
9304 m_buffer = buffer;
603f702b
JS
9305 m_object = NULL;
9306 m_containerAddress.Create(buffer, container);
5d7836c4
JS
9307 m_ignoreThis = ignoreFirstTime;
9308 m_cmdId = id;
9309 m_position = -1;
9310 m_ctrl = ctrl;
9311 m_name = name;
9312 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
9313 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
9314 if (cmd)
9315 cmd->AddAction(this);
9316}
9317
9318wxRichTextAction::~wxRichTextAction()
9319{
603f702b
JS
9320 if (m_object)
9321 delete m_object;
9322}
9323
9324// Returns the container that this action refers to, using the container address and top-level buffer.
9325wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
9326{
9327 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
9328 return container;
5d7836c4
JS
9329}
9330
603f702b 9331
7051fa41
JS
9332void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
9333{
9334 // Store a list of line start character and y positions so we can figure out which area
9335 // we need to refresh
9336
9337#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
9338 wxRichTextParagraphLayoutBox* container = GetContainer();
9339 wxASSERT(container != NULL);
9340 if (!container)
9341 return;
9342
7051fa41
JS
9343 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9344 // If we had several actions, which only invalidate and leave layout until the
9345 // paint handler is called, then this might not be true. So we may need to switch
9346 // optimisation on only when we're simply adding text and not simultaneously
9347 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9348 // first, but of course this means we'll be doing it twice.
603f702b 9349 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41
JS
9350 {
9351 wxSize clientSize = m_ctrl->GetClientSize();
9352 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
9353 int lastY = firstVisiblePt.y + clientSize.y;
9354
603f702b
JS
9355 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
9356 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
9357 while (node)
9358 {
9359 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
9360 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
9361 while (node2)
9362 {
9363 wxRichTextLine* line = node2->GetData();
9364 wxPoint pt = line->GetAbsolutePosition();
9365 wxRichTextRange range = line->GetAbsoluteRange();
9366
9367 if (pt.y > lastY)
9368 {
9369 node2 = wxRichTextLineList::compatibility_iterator();
9370 node = wxRichTextObjectList::compatibility_iterator();
9371 }
9372 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
9373 {
9374 optimizationLineCharPositions.Add(range.GetStart());
9375 optimizationLineYPositions.Add(pt.y);
9376 }
9377
9378 if (node2)
9379 node2 = node2->GetNext();
9380 }
9381
9382 if (node)
9383 node = node->GetNext();
9384 }
9385 }
9386#endif
9387}
9388
5d7836c4
JS
9389bool wxRichTextAction::Do()
9390{
9391 m_buffer->Modify(true);
9392
603f702b
JS
9393 wxRichTextParagraphLayoutBox* container = GetContainer();
9394 wxASSERT(container != NULL);
9395 if (!container)
9396 return false;
9397
5d7836c4
JS
9398 switch (m_cmdId)
9399 {
9400 case wxRICHTEXT_INSERT:
9401 {
ea160b2e
JS
9402 // Store a list of line start character and y positions so we can figure out which area
9403 // we need to refresh
9404 wxArrayInt optimizationLineCharPositions;
9405 wxArrayInt optimizationLineYPositions;
9406
9407#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 9408 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
9409#endif
9410
603f702b
JS
9411 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
9412 container->UpdateRanges();
9413
9414 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9415 // Layout() would stop prematurely at the top level.
9416 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 9417
603f702b 9418 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
9419
9420 // Character position to caret position
9421 newCaretPosition --;
9422
9423 // Don't take into account the last newline
5d7836c4
JS
9424 if (m_newParagraphs.GetPartialParagraph())
9425 newCaretPosition --;
46ee0e5b 9426 else
7c081bd2 9427 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
9428 {
9429 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
9430 if (p->GetRange().GetLength() == 1)
9431 newCaretPosition --;
9432 }
5d7836c4 9433
603f702b 9434 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 9435
7051fa41 9436 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 9437
5912d19e
JS
9438 wxRichTextEvent cmdEvent(
9439 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
9440 m_ctrl ? m_ctrl->GetId() : -1);
9441 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9442 cmdEvent.SetRange(GetRange());
9443 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9444 cmdEvent.SetContainer(container);
3e541562 9445
5912d19e 9446 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
9447
9448 break;
9449 }
9450 case wxRICHTEXT_DELETE:
9451 {
7051fa41
JS
9452 wxArrayInt optimizationLineCharPositions;
9453 wxArrayInt optimizationLineYPositions;
9454
9455#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9456 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9457#endif
9458
603f702b
JS
9459 container->DeleteRange(GetRange());
9460 container->UpdateRanges();
9461 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9462 // Layout() would stop prematurely at the top level.
9463 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 9464
6ccbca24 9465 long caretPos = GetRange().GetStart()-1;
603f702b 9466 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
9467 caretPos --;
9468
7051fa41 9469 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 9470
5912d19e
JS
9471 wxRichTextEvent cmdEvent(
9472 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
9473 m_ctrl ? m_ctrl->GetId() : -1);
9474 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9475 cmdEvent.SetRange(GetRange());
9476 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9477 cmdEvent.SetContainer(container);
3e541562 9478
5912d19e
JS
9479 m_buffer->SendEvent(cmdEvent);
9480
5d7836c4
JS
9481 break;
9482 }
9483 case wxRICHTEXT_CHANGE_STYLE:
9484 {
9485 ApplyParagraphs(GetNewParagraphs());
603f702b
JS
9486
9487 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9488 // Layout() would stop prematurely at the top level.
9489 container->InvalidateHierarchy(GetRange());
9490
9491 UpdateAppearance(GetPosition());
9492
9493 wxRichTextEvent cmdEvent(
9494 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
9495 m_ctrl ? m_ctrl->GetId() : -1);
9496 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9497 cmdEvent.SetRange(GetRange());
9498 cmdEvent.SetPosition(GetRange().GetStart());
9499 cmdEvent.SetContainer(container);
9500
9501 m_buffer->SendEvent(cmdEvent);
9502
9503 break;
9504 }
9505 case wxRICHTEXT_CHANGE_ATTRIBUTES:
9506 {
9507 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
9508 if (obj)
9509 {
9510 wxRichTextAttr oldAttr = obj->GetAttributes();
9511 obj->GetAttributes() = m_attributes;
9512 m_attributes = oldAttr;
9513 }
9514
9515 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9516 // Layout() would stop prematurely at the top level.
9517 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
9518
9519 UpdateAppearance(GetPosition());
9520
5912d19e
JS
9521 wxRichTextEvent cmdEvent(
9522 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
9523 m_ctrl ? m_ctrl->GetId() : -1);
9524 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9525 cmdEvent.SetRange(GetRange());
9526 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9527 cmdEvent.SetContainer(container);
3e541562 9528
5912d19e
JS
9529 m_buffer->SendEvent(cmdEvent);
9530
603f702b
JS
9531 break;
9532 }
9533 case wxRICHTEXT_CHANGE_OBJECT:
9534 {
9535 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
9536 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9537 if (obj && m_object)
9538 {
9539 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
9540 if (node)
9541 {
9542 wxRichTextObject* obj = node->GetData();
9543 node->SetData(m_object);
9544 m_object = obj;
9545 }
9546 }
9547
9548 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9549 // Layout() would stop prematurely at the top level.
9550 container->InvalidateHierarchy(GetRange());
9551
9552 UpdateAppearance(GetPosition());
9553
9554 // TODO: send new kind of modification event
9555
5d7836c4
JS
9556 break;
9557 }
9558 default:
9559 break;
9560 }
9561
9562 return true;
9563}
9564
9565bool wxRichTextAction::Undo()
9566{
9567 m_buffer->Modify(true);
9568
603f702b
JS
9569 wxRichTextParagraphLayoutBox* container = GetContainer();
9570 wxASSERT(container != NULL);
9571 if (!container)
9572 return false;
9573
5d7836c4
JS
9574 switch (m_cmdId)
9575 {
9576 case wxRICHTEXT_INSERT:
9577 {
7051fa41
JS
9578 wxArrayInt optimizationLineCharPositions;
9579 wxArrayInt optimizationLineYPositions;
9580
9581#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9582 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9583#endif
9584
603f702b
JS
9585 container->DeleteRange(GetRange());
9586 container->UpdateRanges();
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(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
9590
9591 long newCaretPosition = GetPosition() - 1;
3e541562 9592
7051fa41 9593 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 9594
5912d19e
JS
9595 wxRichTextEvent cmdEvent(
9596 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
9597 m_ctrl ? m_ctrl->GetId() : -1);
9598 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9599 cmdEvent.SetRange(GetRange());
9600 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9601 cmdEvent.SetContainer(container);
3e541562 9602
5912d19e
JS
9603 m_buffer->SendEvent(cmdEvent);
9604
5d7836c4
JS
9605 break;
9606 }
9607 case wxRICHTEXT_DELETE:
9608 {
7051fa41
JS
9609 wxArrayInt optimizationLineCharPositions;
9610 wxArrayInt optimizationLineYPositions;
9611
9612#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9613 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9614#endif
9615
603f702b
JS
9616 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
9617 container->UpdateRanges();
9618 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9619 // Layout() would stop prematurely at the top level.
9620 container->InvalidateHierarchy(GetRange());
5d7836c4 9621
7051fa41 9622 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 9623
5912d19e
JS
9624 wxRichTextEvent cmdEvent(
9625 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
9626 m_ctrl ? m_ctrl->GetId() : -1);
9627 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9628 cmdEvent.SetRange(GetRange());
9629 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9630 cmdEvent.SetContainer(container);
3e541562 9631
5912d19e
JS
9632 m_buffer->SendEvent(cmdEvent);
9633
5d7836c4
JS
9634 break;
9635 }
9636 case wxRICHTEXT_CHANGE_STYLE:
9637 {
9638 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
9639 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9640 // Layout() would stop prematurely at the top level.
9641 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
9642
9643 UpdateAppearance(GetPosition());
9644
5912d19e
JS
9645 wxRichTextEvent cmdEvent(
9646 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
9647 m_ctrl ? m_ctrl->GetId() : -1);
9648 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9649 cmdEvent.SetRange(GetRange());
9650 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 9651 cmdEvent.SetContainer(container);
3e541562 9652
5912d19e
JS
9653 m_buffer->SendEvent(cmdEvent);
9654
5d7836c4
JS
9655 break;
9656 }
603f702b
JS
9657 case wxRICHTEXT_CHANGE_ATTRIBUTES:
9658 case wxRICHTEXT_CHANGE_OBJECT:
9659 {
9660 return Do();
9661 }
5d7836c4
JS
9662 default:
9663 break;
9664 }
9665
9666 return true;
9667}
9668
9669/// Update the control appearance
603f702b 9670void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 9671{
603f702b
JS
9672 wxRichTextParagraphLayoutBox* container = GetContainer();
9673 wxASSERT(container != NULL);
9674 if (!container)
9675 return;
9676
5d7836c4
JS
9677 if (m_ctrl)
9678 {
603f702b 9679 m_ctrl->SetFocusObject(container);
5d7836c4 9680 m_ctrl->SetCaretPosition(caretPosition);
603f702b 9681
5d7836c4
JS
9682 if (!m_ctrl->IsFrozen())
9683 {
603f702b
JS
9684 wxRect containerRect = container->GetRect();
9685
2f36e8dc 9686 m_ctrl->LayoutContent();
5d7836c4 9687
603f702b
JS
9688 // Refresh everything if there were floating objects or the container changed size
9689 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9690 if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
9691 {
9692 m_ctrl->Refresh(false);
9693 }
9694 else
9695
9696#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9697 // Find refresh rectangle if we are in a position to optimise refresh
9698 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
9699 {
9700 size_t i;
9701
9702 wxSize clientSize = m_ctrl->GetClientSize();
9703 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
9704
9705 // Start/end positions
9706 int firstY = 0;
9707 int lastY = firstVisiblePt.y + clientSize.y;
9708
9709 bool foundEnd = false;
9710
9711 // position offset - how many characters were inserted
9712 int positionOffset = GetRange().GetLength();
9713
9714 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9715 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
9716 positionOffset = - positionOffset;
9717
9718 // find the first line which is being drawn at the same position as it was
9719 // before. Since we're talking about a simple insertion, we can assume
9720 // that the rest of the window does not need to be redrawn.
9721
9722 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
9723 // Since we support floating layout, we should redraw the whole para instead of just
9724 // the first line touching the invalid range.
9725 if (para)
9726 {
9727 firstY = para->GetPosition().y;
9728 }
9729
9730 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
9731 while (node)
9732 {
9733 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
9734 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
9735 while (node2)
9736 {
9737 wxRichTextLine* line = node2->GetData();
9738 wxPoint pt = line->GetAbsolutePosition();
9739 wxRichTextRange range = line->GetAbsoluteRange();
9740
9741 // we want to find the first line that is in the same position
9742 // as before. This will mean we're at the end of the changed text.
9743
9744 if (pt.y > lastY) // going past the end of the window, no more info
9745 {
9746 node2 = wxRichTextLineList::compatibility_iterator();
9747 node = wxRichTextObjectList::compatibility_iterator();
9748 }
9749 // Detect last line in the buffer
9750 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
9751 {
9752 // If deleting text, make sure we refresh below as well as above
9753 if (positionOffset >= 0)
9754 {
9755 foundEnd = true;
9756 lastY = pt.y + line->GetSize().y;
9757 }
9758
9759 node2 = wxRichTextLineList::compatibility_iterator();
9760 node = wxRichTextObjectList::compatibility_iterator();
9761
9762 break;
9763 }
9764 else
9765 {
9766 // search for this line being at the same position as before
9767 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
9768 {
9769 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
9770 ((*optimizationLineYPositions)[i] == pt.y))
9771 {
9772 // Stop, we're now the same as we were
9773 foundEnd = true;
9774
9775 lastY = pt.y;
9776
9777 node2 = wxRichTextLineList::compatibility_iterator();
9778 node = wxRichTextObjectList::compatibility_iterator();
9779
9780 break;
9781 }
9782 }
9783 }
9784
9785 if (node2)
9786 node2 = node2->GetNext();
9787 }
9788
9789 if (node)
9790 node = node->GetNext();
9791 }
9792
9793 firstY = wxMax(firstVisiblePt.y, firstY);
9794 if (!foundEnd)
9795 lastY = firstVisiblePt.y + clientSize.y;
9796
9797 // Convert to device coordinates
9798 wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY));
9799 m_ctrl->RefreshRect(rect);
9800 }
9801 else
1c13f06e 9802#endif
603f702b
JS
9803 m_ctrl->Refresh(false);
9804
9805 m_ctrl->PositionCaret();
9806 m_ctrl->SetDefaultStyleToCursorStyle();
9807
5d7836c4 9808 if (sendUpdateEvent)
0ec1179b 9809 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 9810 }
7fe8059f 9811 }
5d7836c4
JS
9812}
9813
9814/// Replace the buffer paragraphs with the new ones.
0ca07313 9815void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 9816{
603f702b
JS
9817 wxRichTextParagraphLayoutBox* container = GetContainer();
9818 wxASSERT(container != NULL);
9819 if (!container)
9820 return;
9821
5d7836c4
JS
9822 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
9823 while (node)
9824 {
9825 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
9826 wxASSERT (para != NULL);
9827
9828 // We'll replace the existing paragraph by finding the paragraph at this position,
9829 // delete its node data, and setting a copy as the new node data.
9830 // TODO: make more efficient by simply swapping old and new paragraph objects.
9831
603f702b 9832 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
9833 if (existingPara)
9834 {
603f702b 9835 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
9836 if (bufferParaNode)
9837 {
9838 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 9839 newPara->SetParent(container);
5d7836c4
JS
9840
9841 bufferParaNode->SetData(newPara);
9842
9843 delete existingPara;
9844 }
9845 }
9846
9847 node = node->GetNext();
9848 }
9849}
9850
9851
9852/*!
9853 * wxRichTextRange
9854 * This stores beginning and end positions for a range of data.
9855 */
9856
603f702b
JS
9857WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
9858
5d7836c4
JS
9859/// Limit this range to be within 'range'
9860bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
9861{
9862 if (m_start < range.m_start)
9863 m_start = range.m_start;
9864
9865 if (m_end > range.m_end)
9866 m_end = range.m_end;
9867
9868 return true;
9869}
9870
9871/*!
9872 * wxRichTextImage implementation
9873 * This object represents an image.
9874 */
9875
bec80f4f 9876IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 9877
24777478 9878wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 9879 wxRichTextObject(parent)
5d7836c4 9880{
cdaed652 9881 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
9882 if (charStyle)
9883 SetAttributes(*charStyle);
5d7836c4
JS
9884}
9885
24777478 9886wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 9887 wxRichTextObject(parent)
5d7836c4
JS
9888{
9889 m_imageBlock = imageBlock;
4f32b3cf
JS
9890 if (charStyle)
9891 SetAttributes(*charStyle);
5d7836c4
JS
9892}
9893
cdaed652
VZ
9894/// Create a cached image at the required size
9895bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
5d7836c4 9896{
cdaed652
VZ
9897 if (resetCache || !m_imageCache.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
9898 {
9899 if (!m_imageBlock.IsOk())
9900 return false;
ce00f59b 9901
cdaed652
VZ
9902 wxImage image;
9903 m_imageBlock.Load(image);
9904 if (!image.IsOk())
9905 return false;
ce00f59b 9906
cdaed652
VZ
9907 int width = image.GetWidth();
9908 int height = image.GetHeight();
bec80f4f 9909
603f702b 9910 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
cdaed652 9911 {
24777478
JS
9912 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
9913 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
ce00f59b 9914 else
24777478 9915 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
cdaed652 9916 }
603f702b 9917 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
cdaed652 9918 {
24777478
JS
9919 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
9920 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
cdaed652 9921 else
24777478 9922 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
cdaed652 9923 }
5d7836c4 9924
cdaed652
VZ
9925 if (image.GetWidth() == width && image.GetHeight() == height)
9926 m_imageCache = wxBitmap(image);
9927 else
9928 {
9929 // If the original width and height is small, e.g. 400 or below,
9930 // scale up and then down to improve image quality. This can make
9931 // a big difference, with not much performance hit.
9932 int upscaleThreshold = 400;
9933 wxImage img;
9934 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
9935 {
9936 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
9937 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
9938 }
9939 else
9940 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
9941 m_imageCache = wxBitmap(img);
9942 }
9943 }
ce00f59b 9944
cdaed652 9945 return m_imageCache.IsOk();
5d7836c4
JS
9946}
9947
5d7836c4 9948/// Draw the item
603f702b 9949bool wxRichTextImage::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 9950{
603f702b
JS
9951 if (!IsShown())
9952 return true;
9953
cdaed652
VZ
9954 // Don't need cached size AFAIK
9955 // wxSize size = GetCachedSize();
9956 if (!LoadImageCache(dc))
5d7836c4 9957 return false;
ce00f59b 9958
603f702b
JS
9959 DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), wxRect(GetPosition(), GetCachedSize()));
9960
9961#if 0
cdaed652 9962 int y = rect.y + (rect.height - m_imageCache.GetHeight());
5d7836c4 9963
cdaed652 9964 dc.DrawBitmap(m_imageCache, rect.x, y, true);
603f702b
JS
9965#endif
9966
9967 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
9968 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9969 marginRect = rect; // outer rectangle, will calculate contentRect
9970 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
9971
9972 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 9973
603f702b 9974 if (selection.WithinSelection(range.GetStart(), this))
5d7836c4 9975 {
ecb5fbf1
JS
9976 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9977 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 9978 dc.SetLogicalFunction(wxINVERT);
603f702b 9979 dc.DrawRectangle(contentRect);
5d7836c4
JS
9980 dc.SetLogicalFunction(wxCOPY);
9981 }
9982
9983 return true;
9984}
9985
9986/// Lay the item out
cdaed652 9987bool wxRichTextImage::Layout(wxDC& dc, const wxRect& rect, int WXUNUSED(style))
5d7836c4 9988{
cdaed652
VZ
9989 if (!LoadImageCache(dc))
9990 return false;
5d7836c4 9991
603f702b
JS
9992 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
9993 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9994 contentRect = wxRect(wxPoint(0,0), imageSize);
9995 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
9996
9997 wxSize overallSize = marginRect.GetSize();
9998
9999 SetCachedSize(overallSize);
10000 SetMaxSize(overallSize);
10001 SetMinSize(overallSize);
cdaed652 10002 SetPosition(rect.GetPosition());
5d7836c4
JS
10003
10004 return true;
10005}
10006
10007/// Get/set the object size for the given range. Returns false if the range
10008/// is invalid for this object.
cdaed652 10009bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
5d7836c4
JS
10010{
10011 if (!range.IsWithin(GetRange()))
10012 return false;
10013
cdaed652 10014 if (!((wxRichTextImage*)this)->LoadImageCache(dc))
31778480 10015 {
cdaed652
VZ
10016 size.x = 0; size.y = 0;
10017 if (partialExtents)
31778480 10018 partialExtents->Add(0);
cdaed652 10019 return false;
31778480 10020 }
ce00f59b 10021
603f702b
JS
10022 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10023 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10024 contentRect = wxRect(wxPoint(0,0), imageSize);
10025 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
10026
10027 wxSize overallSize = marginRect.GetSize();
31778480 10028
cdaed652 10029 if (partialExtents)
603f702b 10030 partialExtents->Add(overallSize.x);
5d7836c4 10031
603f702b 10032 size = overallSize;
5d7836c4
JS
10033
10034 return true;
10035}
10036
603f702b
JS
10037// Get the 'natural' size for an object. For an image, it would be the
10038// image size.
10039wxTextAttrSize wxRichTextImage::GetNaturalSize() const
10040{
10041 wxTextAttrSize size;
10042 if (GetImageCache().IsOk())
10043 {
10044 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
10045 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
10046 }
10047 return size;
10048}
10049
10050
5d7836c4
JS
10051/// Copy
10052void wxRichTextImage::Copy(const wxRichTextImage& obj)
10053{
bec80f4f 10054 wxRichTextObject::Copy(obj);
59509217 10055
5d7836c4
JS
10056 m_imageBlock = obj.m_imageBlock;
10057}
10058
cdaed652
VZ
10059/// Edit properties via a GUI
10060bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10061{
603f702b
JS
10062 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
10063 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
10064
10065 if (imageDlg.ShowModal() == wxID_OK)
10066 {
603f702b
JS
10067 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10068 // indeterminate in the object.
10069 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
10070 return true;
10071 }
10072 else
10073 return false;
10074}
10075
5d7836c4
JS
10076/*!
10077 * Utilities
10078 *
10079 */
10080
10081/// Compare two attribute objects
24777478 10082bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 10083{
38f833b1 10084 return (attr1 == attr2);
5d7836c4
JS
10085}
10086
44cc96a8 10087// Partial equality test taking flags into account
24777478 10088bool wxTextAttrEqPartial(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
44cc96a8 10089{
24777478 10090 return attr1.EqPartial(attr2);
44cc96a8 10091}
5d7836c4 10092
44cc96a8
JS
10093/// Compare tabs
10094bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
10095{
10096 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
10097 return false;
10098
44cc96a8
JS
10099 size_t i;
10100 for (i = 0; i < tabs1.GetCount(); i++)
10101 {
10102 if (tabs1[i] != tabs2[i])
10103 return false;
10104 }
10105 return true;
10106}
5d7836c4 10107
24777478 10108bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
10109{
10110 return destStyle.Apply(style, compareWith);
10111}
5d7836c4 10112
44cc96a8 10113// Remove attributes
24777478 10114bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 10115{
24777478 10116 return destStyle.RemoveStyle(style);
44cc96a8 10117}
5d7836c4 10118
44cc96a8
JS
10119/// Combine two bitlists, specifying the bits of interest with separate flags.
10120bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
10121{
24777478 10122 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 10123}
5d7836c4 10124
44cc96a8
JS
10125/// Compare two bitlists
10126bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
10127{
24777478 10128 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 10129}
38f833b1 10130
44cc96a8 10131/// Split into paragraph and character styles
24777478 10132bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 10133{
24777478 10134 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 10135}
5d7836c4 10136
44cc96a8
JS
10137/// Convert a decimal to Roman numerals
10138wxString wxRichTextDecimalToRoman(long n)
10139{
10140 static wxArrayInt decimalNumbers;
10141 static wxArrayString romanNumbers;
5d7836c4 10142
44cc96a8
JS
10143 // Clean up arrays
10144 if (n == -1)
10145 {
10146 decimalNumbers.Clear();
10147 romanNumbers.Clear();
10148 return wxEmptyString;
10149 }
5d7836c4 10150
44cc96a8
JS
10151 if (decimalNumbers.GetCount() == 0)
10152 {
10153 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 10154
44cc96a8
JS
10155 wxRichTextAddDecRom(1000, wxT("M"));
10156 wxRichTextAddDecRom(900, wxT("CM"));
10157 wxRichTextAddDecRom(500, wxT("D"));
10158 wxRichTextAddDecRom(400, wxT("CD"));
10159 wxRichTextAddDecRom(100, wxT("C"));
10160 wxRichTextAddDecRom(90, wxT("XC"));
10161 wxRichTextAddDecRom(50, wxT("L"));
10162 wxRichTextAddDecRom(40, wxT("XL"));
10163 wxRichTextAddDecRom(10, wxT("X"));
10164 wxRichTextAddDecRom(9, wxT("IX"));
10165 wxRichTextAddDecRom(5, wxT("V"));
10166 wxRichTextAddDecRom(4, wxT("IV"));
10167 wxRichTextAddDecRom(1, wxT("I"));
10168 }
5d7836c4 10169
44cc96a8
JS
10170 int i = 0;
10171 wxString roman;
ea160b2e 10172
44cc96a8 10173 while (n > 0 && i < 13)
42688aea 10174 {
44cc96a8
JS
10175 if (n >= decimalNumbers[i])
10176 {
10177 n -= decimalNumbers[i];
10178 roman += romanNumbers[i];
10179 }
10180 else
10181 {
10182 i ++;
10183 }
42688aea 10184 }
44cc96a8
JS
10185 if (roman.IsEmpty())
10186 roman = wxT("0");
10187 return roman;
10188}
42688aea 10189
44cc96a8
JS
10190/*!
10191 * wxRichTextFileHandler
10192 * Base class for file handlers
10193 */
4d6d8bf4 10194
44cc96a8 10195IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 10196
44cc96a8
JS
10197#if wxUSE_FFILE && wxUSE_STREAMS
10198bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 10199{
44cc96a8
JS
10200 wxFFileInputStream stream(filename);
10201 if (stream.Ok())
10202 return LoadFile(buffer, stream);
5d7836c4 10203
44cc96a8
JS
10204 return false;
10205}
5d7836c4 10206
44cc96a8
JS
10207bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
10208{
10209 wxFFileOutputStream stream(filename);
10210 if (stream.Ok())
10211 return SaveFile(buffer, stream);
5d7836c4 10212
44cc96a8
JS
10213 return false;
10214}
10215#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 10216
44cc96a8
JS
10217/// Can we handle this filename (if using files)? By default, checks the extension.
10218bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
10219{
10220 wxString path, file, ext;
a51e601e 10221 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 10222
44cc96a8
JS
10223 return (ext.Lower() == GetExtension());
10224}
5d7836c4 10225
44cc96a8
JS
10226/*!
10227 * wxRichTextTextHandler
10228 * Plain text handler
10229 */
5d7836c4 10230
44cc96a8 10231IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 10232
44cc96a8
JS
10233#if wxUSE_STREAMS
10234bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
10235{
10236 if (!stream.IsOk())
797e38dd
JS
10237 return false;
10238
44cc96a8
JS
10239 wxString str;
10240 int lastCh = 0;
5d7836c4 10241
44cc96a8
JS
10242 while (!stream.Eof())
10243 {
10244 int ch = stream.GetC();
5d7836c4 10245
44cc96a8
JS
10246 if (!stream.Eof())
10247 {
10248 if (ch == 10 && lastCh != 13)
10249 str += wxT('\n');
5d7836c4 10250
44cc96a8
JS
10251 if (ch > 0 && ch != 10)
10252 str += wxChar(ch);
5d7836c4 10253
44cc96a8
JS
10254 lastCh = ch;
10255 }
10256 }
5d7836c4 10257
44cc96a8
JS
10258 buffer->ResetAndClearCommands();
10259 buffer->Clear();
10260 buffer->AddParagraphs(str);
10261 buffer->UpdateRanges();
5d7836c4 10262
44cc96a8
JS
10263 return true;
10264}
5d7836c4 10265
44cc96a8
JS
10266bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
10267{
10268 if (!stream.IsOk())
5d7836c4
JS
10269 return false;
10270
44cc96a8 10271 wxString text = buffer->GetText();
38f833b1 10272
44cc96a8
JS
10273 wxString newLine = wxRichTextLineBreakChar;
10274 text.Replace(newLine, wxT("\n"));
5d7836c4 10275
44cc96a8 10276 wxCharBuffer buf = text.ToAscii();
5d7836c4 10277
44cc96a8
JS
10278 stream.Write((const char*) buf, text.length());
10279 return true;
10280}
10281#endif // wxUSE_STREAMS
5d7836c4 10282
44cc96a8
JS
10283/*
10284 * Stores information about an image, in binary in-memory form
10285 */
59509217 10286
44cc96a8
JS
10287wxRichTextImageBlock::wxRichTextImageBlock()
10288{
10289 Init();
10290}
5d7836c4 10291
44cc96a8
JS
10292wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
10293{
10294 Init();
10295 Copy(block);
10296}
ea160b2e 10297
44cc96a8
JS
10298wxRichTextImageBlock::~wxRichTextImageBlock()
10299{
5276b0a5 10300 wxDELETEA(m_data);
5d7836c4
JS
10301}
10302
44cc96a8 10303void wxRichTextImageBlock::Init()
5d7836c4
JS
10304{
10305 m_data = NULL;
10306 m_dataSize = 0;
d75a69e8 10307 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
10308}
10309
10310void wxRichTextImageBlock::Clear()
10311{
5276b0a5 10312 wxDELETEA(m_data);
5d7836c4 10313 m_dataSize = 0;
d75a69e8 10314 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
10315}
10316
10317
10318// Load the original image into a memory block.
10319// If the image is not a JPEG, we must convert it into a JPEG
10320// to conserve space.
10321// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10322// load the image a 2nd time.
10323
d75a69e8
FM
10324bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
10325 wxImage& image, bool convertToJPEG)
5d7836c4
JS
10326{
10327 m_imageType = imageType;
10328
10329 wxString filenameToRead(filename);
7fe8059f 10330 bool removeFile = false;
5d7836c4 10331
62891c87 10332 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 10333 return false; // Could not determine image type
5d7836c4
JS
10334
10335 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
10336 {
a51e601e
FM
10337 wxString tempFile =
10338 wxFileName::CreateTempFileName(_("image"));
5d7836c4 10339
a51e601e 10340 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
10341
10342 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
10343 filenameToRead = tempFile;
7fe8059f 10344 removeFile = true;
5d7836c4
JS
10345
10346 m_imageType = wxBITMAP_TYPE_JPEG;
10347 }
10348 wxFile file;
10349 if (!file.Open(filenameToRead))
7fe8059f 10350 return false;
5d7836c4
JS
10351
10352 m_dataSize = (size_t) file.Length();
10353 file.Close();
10354
10355 if (m_data)
10356 delete[] m_data;
10357 m_data = ReadBlock(filenameToRead, m_dataSize);
10358
10359 if (removeFile)
10360 wxRemoveFile(filenameToRead);
10361
10362 return (m_data != NULL);
10363}
10364
10365// Make an image block from the wxImage in the given
10366// format.
d75a69e8 10367bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 10368{
5d7836c4
JS
10369 image.SetOption(wxT("quality"), quality);
10370
62891c87 10371 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 10372 return false; // Could not determine image type
5d7836c4 10373
cdaed652
VZ
10374 return DoMakeImageBlock(image, imageType);
10375}
10376
10377// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10378bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
10379{
10380 if (imageType == wxBITMAP_TYPE_INVALID)
10381 return false; // Could not determine image type
ce00f59b 10382
cdaed652
VZ
10383 return DoMakeImageBlock(image, imageType);
10384}
7fe8059f 10385
cdaed652
VZ
10386// Makes the image block
10387bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
10388{
10389 wxMemoryOutputStream memStream;
10390 if (!image.SaveFile(memStream, imageType))
5d7836c4 10391 {
7fe8059f 10392 return false;
5d7836c4 10393 }
ce00f59b 10394
cdaed652
VZ
10395 unsigned char* block = new unsigned char[memStream.GetSize()];
10396 if (!block)
377c1ba4 10397 return false;
ce00f59b 10398
5d7836c4
JS
10399 if (m_data)
10400 delete[] m_data;
cdaed652 10401 m_data = block;
ce00f59b
VZ
10402
10403 m_imageType = imageType;
cdaed652 10404 m_dataSize = memStream.GetSize();
5d7836c4 10405
cdaed652 10406 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
10407
10408 return (m_data != NULL);
10409}
10410
5d7836c4
JS
10411// Write to a file
10412bool wxRichTextImageBlock::Write(const wxString& filename)
10413{
10414 return WriteBlock(filename, m_data, m_dataSize);
10415}
10416
10417void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
10418{
10419 m_imageType = block.m_imageType;
5276b0a5 10420 wxDELETEA(m_data);
5d7836c4
JS
10421 m_dataSize = block.m_dataSize;
10422 if (m_dataSize == 0)
10423 return;
10424
10425 m_data = new unsigned char[m_dataSize];
10426 unsigned int i;
10427 for (i = 0; i < m_dataSize; i++)
10428 m_data[i] = block.m_data[i];
10429}
10430
10431//// Operators
10432void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
10433{
10434 Copy(block);
10435}
10436
10437// Load a wxImage from the block
10438bool wxRichTextImageBlock::Load(wxImage& image)
10439{
10440 if (!m_data)
7fe8059f 10441 return false;
5d7836c4
JS
10442
10443 // Read in the image.
0ca07313 10444#if wxUSE_STREAMS
5d7836c4
JS
10445 wxMemoryInputStream mstream(m_data, m_dataSize);
10446 bool success = image.LoadFile(mstream, GetImageType());
10447#else
a51e601e
FM
10448 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
10449 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
10450
10451 if (!WriteBlock(tempFile, m_data, m_dataSize))
10452 {
7fe8059f 10453 return false;
5d7836c4
JS
10454 }
10455 success = image.LoadFile(tempFile, GetImageType());
10456 wxRemoveFile(tempFile);
10457#endif
10458
10459 return success;
10460}
10461
10462// Write data in hex to a stream
10463bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
10464{
351c0647
JS
10465 const int bufSize = 512;
10466 char buf[bufSize+1];
10467
10468 int left = m_dataSize;
10469 int n, i, j;
10470 j = 0;
10471 while (left > 0)
5d7836c4 10472 {
351c0647
JS
10473 if (left*2 > bufSize)
10474 {
10475 n = bufSize; left -= (bufSize/2);
10476 }
10477 else
10478 {
10479 n = left*2; left = 0;
10480 }
7fe8059f 10481
351c0647
JS
10482 char* b = buf;
10483 for (i = 0; i < (n/2); i++)
10484 {
f728025e 10485 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
10486 b += 2; j ++;
10487 }
5d7836c4 10488
351c0647
JS
10489 buf[n] = 0;
10490 stream.Write((const char*) buf, n);
10491 }
5d7836c4
JS
10492 return true;
10493}
10494
10495// Read data in hex from a stream
d75a69e8 10496bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
10497{
10498 int dataSize = length/2;
10499
10500 if (m_data)
10501 delete[] m_data;
10502
046fce47
FM
10503 // create a null terminated temporary string:
10504 char str[3];
10505 str[2] = '\0';
10506
5d7836c4
JS
10507 m_data = new unsigned char[dataSize];
10508 int i;
10509 for (i = 0; i < dataSize; i ++)
10510 {
c9f78968
VS
10511 str[0] = (char)stream.GetC();
10512 str[1] = (char)stream.GetC();
5d7836c4 10513
a9465653 10514 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
10515 }
10516
10517 m_dataSize = dataSize;
10518 m_imageType = imageType;
10519
10520 return true;
10521}
10522
5d7836c4
JS
10523// Allocate and read from stream as a block of memory
10524unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
10525{
10526 unsigned char* block = new unsigned char[size];
10527 if (!block)
10528 return NULL;
10529
10530 stream.Read(block, size);
10531
10532 return block;
10533}
10534
10535unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
10536{
10537 wxFileInputStream stream(filename);
10538 if (!stream.Ok())
10539 return NULL;
10540
10541 return ReadBlock(stream, size);
10542}
10543
10544// Write memory block to stream
10545bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
10546{
10547 stream.Write((void*) block, size);
10548 return stream.IsOk();
10549
10550}
10551
10552// Write memory block to file
10553bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
10554{
10555 wxFileOutputStream outStream(filename);
10556 if (!outStream.Ok())
7fe8059f 10557 return false;
5d7836c4
JS
10558
10559 return WriteBlock(outStream, block, size);
10560}
10561
d2d0adc7
JS
10562// Gets the extension for the block's type
10563wxString wxRichTextImageBlock::GetExtension() const
10564{
10565 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
10566 if (handler)
10567 return handler->GetExtension();
10568 else
10569 return wxEmptyString;
10570}
10571
0ca07313
JS
10572#if wxUSE_DATAOBJ
10573
10574/*!
10575 * The data object for a wxRichTextBuffer
10576 */
10577
10578const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
10579
10580wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
10581{
10582 m_richTextBuffer = richTextBuffer;
10583
10584 // this string should uniquely identify our format, but is otherwise
10585 // arbitrary
10586 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
10587
10588 SetFormat(m_formatRichTextBuffer);
10589}
10590
10591wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10592{
10593 delete m_richTextBuffer;
10594}
10595
10596// after a call to this function, the richTextBuffer is owned by the caller and it
10597// is responsible for deleting it!
10598wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
10599{
10600 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
10601 m_richTextBuffer = NULL;
10602
10603 return richTextBuffer;
10604}
10605
10606wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
10607{
10608 return m_formatRichTextBuffer;
10609}
10610
10611size_t wxRichTextBufferDataObject::GetDataSize() const
10612{
10613 if (!m_richTextBuffer)
10614 return 0;
10615
10616 wxString bufXML;
10617
10618 {
10619 wxStringOutputStream stream(& bufXML);
10620 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
10621 {
10622 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10623 return 0;
10624 }
10625 }
10626
10627#if wxUSE_UNICODE
10628 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
10629 return strlen(buffer) + 1;
10630#else
10631 return bufXML.Length()+1;
10632#endif
10633}
10634
10635bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
10636{
10637 if (!pBuf || !m_richTextBuffer)
10638 return false;
10639
10640 wxString bufXML;
10641
10642 {
10643 wxStringOutputStream stream(& bufXML);
10644 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
10645 {
10646 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10647 return 0;
10648 }
10649 }
10650
10651#if wxUSE_UNICODE
10652 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
10653 size_t len = strlen(buffer);
10654 memcpy((char*) pBuf, (const char*) buffer, len);
10655 ((char*) pBuf)[len] = 0;
10656#else
10657 size_t len = bufXML.Length();
10658 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
10659 ((char*) pBuf)[len] = 0;
10660#endif
10661
10662 return true;
10663}
10664
10665bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
10666{
5276b0a5 10667 wxDELETE(m_richTextBuffer);
0ca07313
JS
10668
10669 wxString bufXML((const char*) buf, wxConvUTF8);
10670
10671 m_richTextBuffer = new wxRichTextBuffer;
10672
10673 wxStringInputStream stream(bufXML);
10674 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
10675 {
10676 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10677
5276b0a5 10678 wxDELETE(m_richTextBuffer);
0ca07313
JS
10679
10680 return false;
10681 }
10682 return true;
10683}
10684
10685#endif
10686 // wxUSE_DATAOBJ
10687
44cc96a8
JS
10688
10689/*
10690 * wxRichTextFontTable
10691 * Manages quick access to a pool of fonts for rendering rich text
10692 */
10693
d65381ac 10694WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
10695
10696class wxRichTextFontTableData: public wxObjectRefData
10697{
10698public:
10699 wxRichTextFontTableData() {}
10700
24777478 10701 wxFont FindFont(const wxRichTextAttr& fontSpec);
44cc96a8
JS
10702
10703 wxRichTextFontTableHashMap m_hashMap;
10704};
10705
24777478 10706wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
10707{
10708 wxString facename(fontSpec.GetFontFaceName());
10709 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()));
10710 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
10711
10712 if ( entry == m_hashMap.end() )
10713 {
10714 wxFont font(fontSpec.GetFontSize(), wxDEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
10715 m_hashMap[spec] = font;
10716 return font;
10717 }
10718 else
10719 {
10720 return entry->second;
10721 }
10722}
10723
10724IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
10725
10726wxRichTextFontTable::wxRichTextFontTable()
10727{
10728 m_refData = new wxRichTextFontTableData;
44cc96a8
JS
10729}
10730
10731wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 10732 : wxObject()
44cc96a8
JS
10733{
10734 (*this) = table;
10735}
10736
10737wxRichTextFontTable::~wxRichTextFontTable()
10738{
10739 UnRef();
10740}
10741
10742bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
10743{
10744 return (m_refData == table.m_refData);
10745}
10746
10747void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
10748{
10749 Ref(table);
10750}
10751
24777478 10752wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
10753{
10754 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
10755 if (data)
10756 return data->FindFont(fontSpec);
10757 else
10758 return wxFont();
10759}
10760
10761void wxRichTextFontTable::Clear()
10762{
10763 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
10764 if (data)
10765 data->m_hashMap.clear();
10766}
10767
24777478
JS
10768// wxTextBoxAttr
10769
10770
10771void wxTextBoxAttr::Reset()
10772{
10773 m_flags = 0;
603f702b
JS
10774 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
10775 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
10776 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
10777 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
bec80f4f 10778
24777478
JS
10779 m_margins.Reset();
10780 m_padding.Reset();
10781 m_position.Reset();
10782
603f702b 10783 m_size.Reset();
24777478
JS
10784
10785 m_border.Reset();
10786 m_outline.Reset();
10787}
10788
10789// Equality test
10790bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
10791{
10792 return (
10793 m_flags == attr.m_flags &&
10794 m_floatMode == attr.m_floatMode &&
10795 m_clearMode == attr.m_clearMode &&
10796 m_collapseMode == attr.m_collapseMode &&
603f702b 10797 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 10798
24777478
JS
10799 m_margins == attr.m_margins &&
10800 m_padding == attr.m_padding &&
10801 m_position == attr.m_position &&
10802
603f702b 10803 m_size == attr.m_size &&
24777478
JS
10804
10805 m_border == attr.m_border &&
10806 m_outline == attr.m_outline
10807 );
10808}
10809
10810// Partial equality test
10811bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr) const
10812{
10813 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
10814 return false;
10815
10816 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
10817 return false;
10818
10819 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
10820 return false;
10821
603f702b
JS
10822 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
10823 return false;
10824
24777478
JS
10825 // Position
10826
10827 if (!m_position.EqPartial(attr.m_position))
10828 return false;
10829
10830 // Margins
10831
10832 if (!m_margins.EqPartial(attr.m_margins))
10833 return false;
10834
10835 // Padding
10836
10837 if (!m_padding.EqPartial(attr.m_padding))
10838 return false;
10839
10840 // Border
10841
10842 if (!GetBorder().EqPartial(attr.GetBorder()))
10843 return false;
10844
10845 // Outline
10846
10847 if (!GetOutline().EqPartial(attr.GetOutline()))
10848 return false;
10849
10850 return true;
10851}
10852
10853// Merges the given attributes. If compareWith
10854// is non-NULL, then it will be used to mask out those attributes that are the same in style
10855// and compareWith, for situations where we don't want to explicitly set inherited attributes.
10856bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
10857{
10858 if (attr.HasFloatMode())
10859 {
10860 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
10861 SetFloatMode(attr.GetFloatMode());
10862 }
10863
10864 if (attr.HasClearMode())
10865 {
10866 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
10867 SetClearMode(attr.GetClearMode());
10868 }
10869
10870 if (attr.HasCollapseBorders())
10871 {
10872 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
10873 SetCollapseBorders(attr.GetCollapseBorders());
10874 }
10875
10876 if (attr.HasVerticalAlignment())
10877 {
10878 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
10879 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 10880 }
bec80f4f
JS
10881
10882 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
10883 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
10884 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 10885
603f702b 10886 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
24777478 10887
bec80f4f
JS
10888 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
10889 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
10890
10891 return true;
10892}
10893
10894// Remove specified attributes from this object
10895bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
10896{
10897 if (attr.HasFloatMode())
10898 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
10899
10900 if (attr.HasClearMode())
10901 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
10902
10903 if (attr.HasCollapseBorders())
10904 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
10905
603f702b
JS
10906 if (attr.HasVerticalAlignment())
10907 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
10908
24777478
JS
10909 m_margins.RemoveStyle(attr.m_margins);
10910 m_padding.RemoveStyle(attr.m_padding);
10911 m_position.RemoveStyle(attr.m_position);
10912
603f702b 10913 m_size.RemoveStyle(attr.m_size);
24777478
JS
10914
10915 m_border.RemoveStyle(attr.m_border);
10916 m_outline.RemoveStyle(attr.m_outline);
10917
10918 return true;
10919}
10920
10921// Collects the attributes that are common to a range of content, building up a note of
10922// which attributes are absent in some objects and which clash in some objects.
10923void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
10924{
10925 if (attr.HasFloatMode())
10926 {
10927 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
10928 {
10929 if (HasFloatMode())
10930 {
10931 if (GetFloatMode() != attr.GetFloatMode())
10932 {
10933 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
10934 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
10935 }
10936 }
10937 else
10938 SetFloatMode(attr.GetFloatMode());
10939 }
10940 }
10941 else
10942 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 10943
24777478
JS
10944 if (attr.HasClearMode())
10945 {
10946 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
10947 {
10948 if (HasClearMode())
10949 {
10950 if (GetClearMode() != attr.GetClearMode())
10951 {
10952 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
10953 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
10954 }
10955 }
10956 else
10957 SetClearMode(attr.GetClearMode());
10958 }
10959 }
10960 else
10961 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
10962
10963 if (attr.HasCollapseBorders())
10964 {
10965 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
10966 {
10967 if (HasCollapseBorders())
10968 {
10969 if (GetCollapseBorders() != attr.GetCollapseBorders())
10970 {
10971 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
10972 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
10973 }
10974 }
10975 else
10976 SetCollapseBorders(attr.GetCollapseBorders());
10977 }
10978 }
10979 else
10980 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 10981
603f702b
JS
10982 if (attr.HasVerticalAlignment())
10983 {
10984 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
10985 {
10986 if (HasVerticalAlignment())
10987 {
10988 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
10989 {
10990 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
10991 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
10992 }
10993 }
10994 else
10995 SetVerticalAlignment(attr.GetVerticalAlignment());
10996 }
10997 }
10998 else
10999 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11000
24777478
JS
11001 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
11002 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
11003 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
11004
603f702b 11005 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
24777478
JS
11006
11007 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
11008 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
11009}
11010
11011// wxRichTextAttr
11012
11013void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
11014{
bec80f4f
JS
11015 wxTextAttr::Copy(attr);
11016
24777478
JS
11017 m_textBoxAttr = attr.m_textBoxAttr;
11018}
11019
11020bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
11021{
11022 if (!(wxTextAttr::operator==(attr)))
11023 return false;
bec80f4f 11024
24777478
JS
11025 return (m_textBoxAttr == attr.m_textBoxAttr);
11026}
11027
11028// Partial equality test taking comparison object into account
11029bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const
11030{
11031 if (!(wxTextAttr::EqPartial(attr)))
11032 return false;
bec80f4f 11033
24777478
JS
11034 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr);
11035}
11036
11037// Merges the given attributes. If compareWith
11038// is non-NULL, then it will be used to mask out those attributes that are the same in style
11039// and compareWith, for situations where we don't want to explicitly set inherited attributes.
11040bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
11041{
11042 wxTextAttr::Apply(style, compareWith);
11043
11044 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
11045}
11046
11047// Remove specified attributes from this object
11048bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
11049{
11050 wxTextAttr::RemoveStyle(*this, attr);
11051
11052 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
11053}
11054
11055// Collects the attributes that are common to a range of content, building up a note of
11056// which attributes are absent in some objects and which clash in some objects.
11057void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
11058{
11059 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 11060
24777478
JS
11061 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
11062}
11063
11064// Partial equality test
bec80f4f 11065bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border) const
24777478
JS
11066{
11067 if (border.HasStyle() && !HasStyle() && (border.GetStyle() != GetStyle()))
11068 return false;
11069
11070 if (border.HasColour() && !HasColour() && (border.GetColourLong() != GetColourLong()))
11071 return false;
11072
11073 if (border.HasWidth() && !HasWidth() && !(border.GetWidth() == GetWidth()))
11074 return false;
11075
11076 return true;
11077}
11078
11079// Apply border to 'this', but not if the same as compareWith
bec80f4f 11080bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
11081{
11082 if (border.HasStyle())
11083 {
11084 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
11085 SetStyle(border.GetStyle());
11086 }
11087 if (border.HasColour())
11088 {
11089 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
11090 SetColour(border.GetColourLong());
11091 }
11092 if (border.HasWidth())
11093 {
11094 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
11095 SetWidth(border.GetWidth());
11096 }
11097
11098 return true;
11099}
11100
11101// Remove specified attributes from this object
bec80f4f 11102bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
11103{
11104 if (attr.HasStyle() && HasStyle())
11105 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
11106 if (attr.HasColour() && HasColour())
11107 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
11108 if (attr.HasWidth() && HasWidth())
11109 m_borderWidth.Reset();
11110
11111 return true;
11112}
11113
11114// Collects the attributes that are common to a range of content, building up a note of
11115// which attributes are absent in some objects and which clash in some objects.
bec80f4f 11116void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
11117{
11118 if (attr.HasStyle())
11119 {
11120 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
11121 {
11122 if (HasStyle())
11123 {
11124 if (GetStyle() != attr.GetStyle())
11125 {
11126 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11127 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11128 }
11129 }
11130 else
11131 SetStyle(attr.GetStyle());
11132 }
11133 }
11134 else
11135 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11136
11137 if (attr.HasColour())
11138 {
11139 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
11140 {
11141 if (HasColour())
11142 {
11143 if (GetColour() != attr.GetColour())
11144 {
11145 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
11146 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
11147 }
11148 }
11149 else
11150 SetColour(attr.GetColourLong());
11151 }
11152 }
11153 else
11154 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 11155
24777478
JS
11156 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
11157}
11158
11159// Partial equality test
bec80f4f 11160bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders) const
24777478
JS
11161{
11162 return m_left.EqPartial(borders.m_left) && m_right.EqPartial(borders.m_right) &&
11163 m_top.EqPartial(borders.m_top) && m_bottom.EqPartial(borders.m_bottom);
11164}
11165
11166// Apply border to 'this', but not if the same as compareWith
bec80f4f 11167bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 11168{
bec80f4f
JS
11169 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
11170 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
11171 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
11172 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
11173 return true;
11174}
11175
11176// Remove specified attributes from this object
bec80f4f 11177bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
11178{
11179 m_left.RemoveStyle(attr.m_left);
11180 m_right.RemoveStyle(attr.m_right);
11181 m_top.RemoveStyle(attr.m_top);
11182 m_bottom.RemoveStyle(attr.m_bottom);
11183 return true;
11184}
11185
11186// Collects the attributes that are common to a range of content, building up a note of
11187// which attributes are absent in some objects and which clash in some objects.
bec80f4f 11188void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
11189{
11190 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
11191 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
11192 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
11193 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
11194}
11195
11196// Set style of all borders
bec80f4f 11197void wxTextAttrBorders::SetStyle(int style)
24777478
JS
11198{
11199 m_left.SetStyle(style);
11200 m_right.SetStyle(style);
11201 m_top.SetStyle(style);
11202 m_bottom.SetStyle(style);
11203}
11204
11205// Set colour of all borders
bec80f4f 11206void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
11207{
11208 m_left.SetColour(colour);
11209 m_right.SetColour(colour);
11210 m_top.SetColour(colour);
11211 m_bottom.SetColour(colour);
11212}
11213
bec80f4f 11214void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
11215{
11216 m_left.SetColour(colour);
11217 m_right.SetColour(colour);
11218 m_top.SetColour(colour);
11219 m_bottom.SetColour(colour);
11220}
11221
11222// Set width of all borders
bec80f4f 11223void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
11224{
11225 m_left.SetWidth(width);
11226 m_right.SetWidth(width);
11227 m_top.SetWidth(width);
11228 m_bottom.SetWidth(width);
11229}
11230
11231// Partial equality test
11232bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim) const
11233{
603f702b 11234 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
11235 return false;
11236 else
11237 return true;
11238}
11239
11240bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
11241{
603f702b 11242 if (dim.IsValid())
24777478
JS
11243 {
11244 if (!(compareWith && dim == (*compareWith)))
11245 (*this) = dim;
11246 }
11247
11248 return true;
11249}
11250
11251// Collects the attributes that are common to a range of content, building up a note of
11252// which attributes are absent in some objects and which clash in some objects.
11253void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
11254{
603f702b 11255 if (attr.IsValid())
24777478 11256 {
603f702b 11257 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 11258 {
603f702b 11259 if (IsValid())
24777478
JS
11260 {
11261 if (!((*this) == attr))
11262 {
603f702b
JS
11263 clashingAttr.SetValid(true);
11264 SetValid(false);
24777478
JS
11265 }
11266 }
11267 else
11268 (*this) = attr;
11269 }
11270 }
11271 else
603f702b 11272 absentAttr.SetValid(true);
24777478
JS
11273}
11274
8995db52
JS
11275wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
11276{
11277 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
11278}
11279
11280wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
11281{
11282 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
11283}
11284
bec80f4f
JS
11285int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
11286{
11287 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
11288}
11289
11290int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
11291{
11292 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
11293}
11294
11295int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
11296{
11297 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11298 return ConvertTenthsMMToPixels(dim.GetValue());
11299 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11300 return dim.GetValue();
11301 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11302 {
11303 wxASSERT(m_parentSize != wxDefaultSize);
11304 if (direction == wxHORIZONTAL)
11305 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
11306 else
11307 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
11308 }
11309 else
11310 {
11311 wxASSERT(false);
11312 return 0;
11313 }
11314}
11315
11316int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
11317{
11318 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11319 return dim.GetValue();
11320 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11321 return ConvertPixelsToTenthsMM(dim.GetValue());
11322 else
11323 {
11324 wxASSERT(false);
11325 return 0;
11326 }
11327}
11328
24777478 11329// Partial equality test
bec80f4f 11330bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims) const
24777478
JS
11331{
11332 if (!m_left.EqPartial(dims.m_left))
11333 return false;
11334
11335 if (!m_right.EqPartial(dims.m_right))
11336 return false;
11337
11338 if (!m_top.EqPartial(dims.m_top))
11339 return false;
11340
11341 if (!m_bottom.EqPartial(dims.m_bottom))
11342 return false;
11343
11344 return true;
11345}
11346
11347// Apply border to 'this', but not if the same as compareWith
bec80f4f 11348bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
11349{
11350 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
11351 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
11352 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
11353 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
11354
11355 return true;
11356}
11357
11358// Remove specified attributes from this object
bec80f4f 11359bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 11360{
603f702b 11361 if (attr.m_left.IsValid())
24777478 11362 m_left.Reset();
603f702b 11363 if (attr.m_right.IsValid())
24777478 11364 m_right.Reset();
603f702b 11365 if (attr.m_top.IsValid())
24777478 11366 m_top.Reset();
603f702b 11367 if (attr.m_bottom.IsValid())
24777478
JS
11368 m_bottom.Reset();
11369
11370 return true;
11371}
11372
11373// Collects the attributes that are common to a range of content, building up a note of
11374// which attributes are absent in some objects and which clash in some objects.
bec80f4f 11375void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
11376{
11377 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
11378 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
11379 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
11380 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
11381}
11382
603f702b
JS
11383// Partial equality test
11384bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size) const
11385{
11386 if (!m_width.EqPartial(size.m_width))
11387 return false;
11388
11389 if (!m_height.EqPartial(size.m_height))
11390 return false;
11391
11392 return true;
11393}
11394
11395// Apply border to 'this', but not if the same as compareWith
11396bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
11397{
11398 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
11399 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
11400
11401 return true;
11402}
11403
11404// Remove specified attributes from this object
11405bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
11406{
11407 if (attr.m_width.IsValid())
11408 m_width.Reset();
11409 if (attr.m_height.IsValid())
11410 m_height.Reset();
11411
11412 return true;
11413}
11414
11415// Collects the attributes that are common to a range of content, building up a note of
11416// which attributes are absent in some objects and which clash in some objects.
11417void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
11418{
11419 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
11420 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
11421}
11422
24777478
JS
11423// Collects the attributes that are common to a range of content, building up a note of
11424// which attributes are absent in some objects and which clash in some objects.
11425void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
11426{
11427 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
11428 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
11429
11430 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
11431
11432 if (attr.HasFont())
11433 {
11434 if (attr.HasFontSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_SIZE))
11435 {
11436 if (currentStyle.HasFontSize())
11437 {
11438 if (currentStyle.GetFontSize() != attr.GetFontSize())
11439 {
11440 // Clash of attr - mark as such
11441 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
11442 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
11443 }
11444 }
11445 else
11446 currentStyle.SetFontSize(attr.GetFontSize());
11447 }
11448
11449 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
11450 {
11451 if (currentStyle.HasFontItalic())
11452 {
11453 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
11454 {
11455 // Clash of attr - mark as such
11456 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
11457 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
11458 }
11459 }
11460 else
11461 currentStyle.SetFontStyle(attr.GetFontStyle());
11462 }
11463
11464 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
11465 {
11466 if (currentStyle.HasFontFamily())
11467 {
11468 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
11469 {
11470 // Clash of attr - mark as such
11471 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
11472 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
11473 }
11474 }
11475 else
11476 currentStyle.SetFontFamily(attr.GetFontFamily());
11477 }
11478
11479 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
11480 {
11481 if (currentStyle.HasFontWeight())
11482 {
11483 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
11484 {
11485 // Clash of attr - mark as such
11486 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
11487 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
11488 }
11489 }
11490 else
11491 currentStyle.SetFontWeight(attr.GetFontWeight());
11492 }
11493
11494 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
11495 {
11496 if (currentStyle.HasFontFaceName())
11497 {
11498 wxString faceName1(currentStyle.GetFontFaceName());
11499 wxString faceName2(attr.GetFontFaceName());
11500
11501 if (faceName1 != faceName2)
11502 {
11503 // Clash of attr - mark as such
11504 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
11505 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
11506 }
11507 }
11508 else
11509 currentStyle.SetFontFaceName(attr.GetFontFaceName());
11510 }
11511
11512 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
11513 {
11514 if (currentStyle.HasFontUnderlined())
11515 {
11516 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
11517 {
11518 // Clash of attr - mark as such
11519 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
11520 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
11521 }
11522 }
11523 else
11524 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
11525 }
11526 }
11527
11528 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
11529 {
11530 if (currentStyle.HasTextColour())
11531 {
11532 if (currentStyle.GetTextColour() != attr.GetTextColour())
11533 {
11534 // Clash of attr - mark as such
11535 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
11536 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
11537 }
11538 }
11539 else
11540 currentStyle.SetTextColour(attr.GetTextColour());
11541 }
11542
11543 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
11544 {
11545 if (currentStyle.HasBackgroundColour())
11546 {
11547 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
11548 {
11549 // Clash of attr - mark as such
11550 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
11551 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
11552 }
11553 }
11554 else
11555 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
11556 }
11557
11558 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
11559 {
11560 if (currentStyle.HasAlignment())
11561 {
11562 if (currentStyle.GetAlignment() != attr.GetAlignment())
11563 {
11564 // Clash of attr - mark as such
11565 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
11566 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
11567 }
11568 }
11569 else
11570 currentStyle.SetAlignment(attr.GetAlignment());
11571 }
11572
11573 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
11574 {
11575 if (currentStyle.HasTabs())
11576 {
11577 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
11578 {
11579 // Clash of attr - mark as such
11580 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
11581 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
11582 }
11583 }
11584 else
11585 currentStyle.SetTabs(attr.GetTabs());
11586 }
11587
11588 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
11589 {
11590 if (currentStyle.HasLeftIndent())
11591 {
11592 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
11593 {
11594 // Clash of attr - mark as such
11595 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
11596 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
11597 }
11598 }
11599 else
11600 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
11601 }
11602
11603 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
11604 {
11605 if (currentStyle.HasRightIndent())
11606 {
11607 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
11608 {
11609 // Clash of attr - mark as such
11610 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
11611 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
11612 }
11613 }
11614 else
11615 currentStyle.SetRightIndent(attr.GetRightIndent());
11616 }
11617
11618 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
11619 {
11620 if (currentStyle.HasParagraphSpacingAfter())
11621 {
11622 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
11623 {
11624 // Clash of attr - mark as such
11625 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
11626 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
11627 }
11628 }
11629 else
11630 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
11631 }
11632
11633 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
11634 {
11635 if (currentStyle.HasParagraphSpacingBefore())
11636 {
11637 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
11638 {
11639 // Clash of attr - mark as such
11640 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
11641 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
11642 }
11643 }
11644 else
11645 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
11646 }
11647
11648 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
11649 {
11650 if (currentStyle.HasLineSpacing())
11651 {
11652 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
11653 {
11654 // Clash of attr - mark as such
11655 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
11656 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
11657 }
11658 }
11659 else
11660 currentStyle.SetLineSpacing(attr.GetLineSpacing());
11661 }
11662
11663 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
11664 {
11665 if (currentStyle.HasCharacterStyleName())
11666 {
11667 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
11668 {
11669 // Clash of attr - mark as such
11670 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
11671 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
11672 }
11673 }
11674 else
11675 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
11676 }
11677
11678 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
11679 {
11680 if (currentStyle.HasParagraphStyleName())
11681 {
11682 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
11683 {
11684 // Clash of attr - mark as such
11685 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
11686 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
11687 }
11688 }
11689 else
11690 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
11691 }
11692
11693 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
11694 {
11695 if (currentStyle.HasListStyleName())
11696 {
11697 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
11698 {
11699 // Clash of attr - mark as such
11700 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
11701 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
11702 }
11703 }
11704 else
11705 currentStyle.SetListStyleName(attr.GetListStyleName());
11706 }
11707
11708 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
11709 {
11710 if (currentStyle.HasBulletStyle())
11711 {
11712 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
11713 {
11714 // Clash of attr - mark as such
11715 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
11716 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
11717 }
11718 }
11719 else
11720 currentStyle.SetBulletStyle(attr.GetBulletStyle());
11721 }
11722
11723 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
11724 {
11725 if (currentStyle.HasBulletNumber())
11726 {
11727 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
11728 {
11729 // Clash of attr - mark as such
11730 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
11731 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
11732 }
11733 }
11734 else
11735 currentStyle.SetBulletNumber(attr.GetBulletNumber());
11736 }
11737
11738 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
11739 {
11740 if (currentStyle.HasBulletText())
11741 {
11742 if (currentStyle.GetBulletText() != attr.GetBulletText())
11743 {
11744 // Clash of attr - mark as such
11745 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
11746 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
11747 }
11748 }
11749 else
11750 {
11751 currentStyle.SetBulletText(attr.GetBulletText());
11752 currentStyle.SetBulletFont(attr.GetBulletFont());
11753 }
11754 }
11755
11756 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
11757 {
11758 if (currentStyle.HasBulletName())
11759 {
11760 if (currentStyle.GetBulletName() != attr.GetBulletName())
11761 {
11762 // Clash of attr - mark as such
11763 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
11764 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
11765 }
11766 }
11767 else
11768 {
11769 currentStyle.SetBulletName(attr.GetBulletName());
11770 }
11771 }
11772
11773 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
11774 {
11775 if (currentStyle.HasURL())
11776 {
11777 if (currentStyle.GetURL() != attr.GetURL())
11778 {
11779 // Clash of attr - mark as such
11780 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
11781 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
11782 }
11783 }
11784 else
11785 {
11786 currentStyle.SetURL(attr.GetURL());
11787 }
11788 }
11789
11790 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
11791 {
11792 if (currentStyle.HasTextEffects())
11793 {
11794 // We need to find the bits in the new attr that are different:
11795 // just look at those bits that are specified by the new attr.
11796
11797 // We need to remove the bits and flags that are not common between current attr
11798 // and new attr. In so doing we need to take account of the styles absent from one or more of the
11799 // previous styles.
11800
11801 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
11802 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
11803
11804 if (currentRelevantTextEffects != newRelevantTextEffects)
11805 {
11806 // Find the text effects that were different, using XOR
11807 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
11808
11809 // Clash of attr - mark as such
11810 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
11811 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
11812 }
11813 }
11814 else
11815 {
11816 currentStyle.SetTextEffects(attr.GetTextEffects());
11817 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
11818 }
11819
11820 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
11821 // that we've looked at so far
11822 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
11823 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
11824
11825 if (currentStyle.GetTextEffectFlags() == 0)
11826 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
11827 }
11828
11829 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
11830 {
11831 if (currentStyle.HasOutlineLevel())
11832 {
11833 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
11834 {
11835 // Clash of attr - mark as such
11836 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
11837 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
11838 }
11839 }
11840 else
11841 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
11842 }
11843}
11844
bec80f4f
JS
11845WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
11846
11847IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
11848
11849bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
11850{
11851 if (m_properties.GetCount() != props.GetCount())
11852 return false;
11853
11854 size_t i;
11855 for (i = 0; i < m_properties.GetCount(); i++)
11856 {
11857 const wxVariant& var1 = m_properties[i];
11858 int idx = props.Find(var1.GetName());
11859 if (idx == -1)
11860 return false;
11861 const wxVariant& var2 = props.m_properties[idx];
11862 if (!(var1 == var2))
11863 return false;
11864 }
11865
11866 return true;
11867}
11868
11869wxArrayString wxRichTextProperties::GetPropertyNames() const
11870{
11871 wxArrayString arr;
11872 size_t i;
11873 for (i = 0; i < m_properties.GetCount(); i++)
11874 {
11875 arr.Add(m_properties[i].GetName());
11876 }
11877 return arr;
11878}
11879
11880int wxRichTextProperties::Find(const wxString& name) const
11881{
11882 size_t i;
11883 for (i = 0; i < m_properties.GetCount(); i++)
11884 {
11885 if (m_properties[i].GetName() == name)
11886 return (int) i;
11887 }
11888 return -1;
11889}
11890
11891wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
11892{
11893 int idx = Find(name);
11894 if (idx == wxNOT_FOUND)
11895 SetProperty(name, wxString());
11896 idx = Find(name);
11897 if (idx != wxNOT_FOUND)
11898 {
11899 return & (*this)[idx];
11900 }
11901 else
11902 return NULL;
11903}
11904
11905const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
11906{
11907 static const wxVariant nullVariant;
11908 int idx = Find(name);
11909 if (idx != -1)
11910 return m_properties[idx];
11911 else
11912 return nullVariant;
11913}
11914
11915wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
11916{
11917 return GetProperty(name).GetString();
11918}
11919
11920long wxRichTextProperties::GetPropertyLong(const wxString& name) const
11921{
11922 return GetProperty(name).GetLong();
11923}
11924
11925bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
11926{
11927 return GetProperty(name).GetBool();
11928}
11929
11930double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
11931{
11932 return GetProperty(name).GetDouble();
11933}
11934
11935void wxRichTextProperties::SetProperty(const wxVariant& variant)
11936{
11937 wxASSERT(!variant.GetName().IsEmpty());
11938
11939 int idx = Find(variant.GetName());
11940
11941 if (idx == -1)
11942 m_properties.Add(variant);
11943 else
11944 m_properties[idx] = variant;
11945}
11946
11947void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
11948{
11949 int idx = Find(name);
11950 wxVariant var(variant);
11951 var.SetName(name);
11952
11953 if (idx == -1)
11954 m_properties.Add(var);
11955 else
11956 m_properties[idx] = var;
11957}
11958
11959void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
11960{
11961 SetProperty(name, wxVariant(value, name));
11962}
11963
11964void wxRichTextProperties::SetProperty(const wxString& name, long value)
11965{
11966 SetProperty(name, wxVariant(value, name));
11967}
11968
11969void wxRichTextProperties::SetProperty(const wxString& name, double value)
11970{
11971 SetProperty(name, wxVariant(value, name));
11972}
11973
11974void wxRichTextProperties::SetProperty(const wxString& name, bool value)
11975{
11976 SetProperty(name, wxVariant(value, name));
11977}
24777478 11978
603f702b
JS
11979wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
11980{
11981 if (m_address.GetCount() == 0)
11982 return topLevelContainer;
11983
11984 wxRichTextCompositeObject* p = topLevelContainer;
11985 size_t i = 0;
11986 while (p && i < m_address.GetCount())
11987 {
11988 int pos = m_address[i];
11989 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
11990 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
11991 return NULL;
11992
11993 wxRichTextObject* p1 = p->GetChild(pos);
11994 if (i == (m_address.GetCount()-1))
11995 return p1;
11996
11997 p = wxDynamicCast(p1, wxRichTextCompositeObject);
11998 i ++;
11999 }
12000 return NULL;
12001}
12002
12003bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
12004{
12005 m_address.Clear();
12006
12007 if (topLevelContainer == obj)
12008 return true;
12009
12010 wxRichTextObject* o = obj;
12011 while (o)
12012 {
12013 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
12014 if (!p)
12015 return false;
12016
12017 int pos = p->GetChildren().IndexOf(o);
12018 if (pos == -1)
12019 return false;
12020
12021 m_address.Insert(pos, 0);
12022
12023 if (p == topLevelContainer)
12024 return true;
12025
12026 o = p;
12027 }
12028 return false;
12029}
12030
12031// Equality test
12032bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
12033{
12034 if (m_container != sel.m_container)
12035 return false;
12036 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
12037 return false;
12038 size_t i;
12039 for (i = 0; i < m_ranges.GetCount(); i++)
12040 if (!(m_ranges[i] == sel.m_ranges[i]))
12041 return false;
12042 return true;
12043}
12044
12045// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
12046// or none at the level of the object's container.
12047wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
12048{
12049 if (IsValid())
12050 {
12051 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
12052
12053 if (container == m_container)
12054 return m_ranges;
12055
12056 container = obj->GetContainer();
12057 while (container)
12058 {
12059 if (container->GetParent())
12060 {
12061 // If we found that our object's container is within the range of
12062 // a selection higher up, then assume the whole original object
12063 // is also selected.
12064 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
12065 if (parentContainer == m_container)
12066 {
12067 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
12068 {
12069 wxRichTextRangeArray ranges;
12070 ranges.Add(obj->GetRange());
12071 return ranges;
12072 }
12073 }
12074
12075 container = parentContainer;
12076 }
12077 else
12078 {
12079 container = NULL;
12080 break;
12081 }
12082 }
12083 }
12084 return wxRichTextRangeArray();
12085}
12086
12087// Is the given position within the selection?
12088bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
12089{
12090 if (!IsValid())
12091 return false;
12092 else
12093 {
12094 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
12095 return WithinSelection(pos, selectionRanges);
12096 }
12097}
12098
12099// Is the given position within the selection range?
12100bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
12101{
12102 size_t i;
12103 for (i = 0; i < ranges.GetCount(); i++)
12104 {
12105 const wxRichTextRange& range = ranges[i];
12106 if (pos >= range.GetStart() && pos <= range.GetEnd())
12107 return true;
12108 }
12109 return false;
12110}
12111
12112// Is the given range completely within the selection range?
12113bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
12114{
12115 size_t i;
12116 for (i = 0; i < ranges.GetCount(); i++)
12117 {
12118 const wxRichTextRange& eachRange = ranges[i];
12119 if (range.IsWithin(eachRange))
12120 return true;
12121 }
12122 return false;
12123}
12124
12125
5d7836c4
JS
12126#endif
12127 // wxUSE_RICHTEXT