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