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