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