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