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