]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
Remove unnecessary base class OnPaint() call from wxGenericColourDialog.
[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"
1aca9fcd 45#include "wx/richtext/richtextxml.h"
5d7836c4
JS
46
47#include "wx/listimpl.cpp"
bec80f4f 48#include "wx/arrimpl.cpp"
5d7836c4 49
412e0d47
DS
50WX_DEFINE_LIST(wxRichTextObjectList)
51WX_DEFINE_LIST(wxRichTextLineList)
5d7836c4 52
ea160b2e
JS
53// Switch off if the platform doesn't like it for some reason
54#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55
31778480
JS
56// Use GetPartialTextExtents for platforms that support it natively
57#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58
ff76711f
JS
59const wxChar wxRichTextLineBreakChar = (wxChar) 29;
60
cdaed652
VZ
61// Helper classes for floating layout
62struct wxRichTextFloatRectMap
63{
64 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
65 {
66 startY = sY;
67 endY = eY;
68 width = w;
69 anchor = obj;
70 }
71
72 int startY, endY;
73 int width;
74 wxRichTextObject* anchor;
75};
76
77WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
78
79int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
80{
81 return r1->startY - r2->startY;
82}
83
84class wxRichTextFloatCollector
85{
86public:
603f702b 87 wxRichTextFloatCollector(const wxRect& availableRect);
cdaed652
VZ
88 ~wxRichTextFloatCollector();
89
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph* para);
92 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
93
94 // Return the last paragraph we collected
95 wxRichTextParagraph* LastParagraph();
96
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect GetAvailableRect(int startY, int endY);
100
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction, int start, int height) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
104
105 // Find the last y position
106 int GetLastRectBottom();
107
108 // Draw the floats inside a rect
8db2e3ef 109 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652
VZ
110
111 // HitTest the floats
8db2e3ef 112 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
603f702b
JS
113
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
116
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
cdaed652 119
07d4142f
JS
120 // Delete a float
121 bool DeleteFloat(wxRichTextObject* obj);
122
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject* obj);
125
126 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
127
cdaed652
VZ
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
129
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
ce00f59b 131
cdaed652
VZ
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
133
8db2e3ef 134 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652 135
8db2e3ef 136 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
ce00f59b 137
cdaed652
VZ
138private:
139 wxRichTextFloatRectMapArray m_left;
140 wxRichTextFloatRectMapArray m_right;
603f702b
JS
141 //int m_width;
142 wxRect m_availableRect;
cdaed652
VZ
143 wxRichTextParagraph* m_para;
144};
145
07d4142f
JS
146// Delete a float
147bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
148{
149 size_t i;
150 for (i = 0; i < m_left.GetCount(); i++)
151 {
152 if (m_left[i]->anchor == obj)
153 {
154 m_left.RemoveAt(i);
155 return true;
156 }
157 }
158 for (i = 0; i < m_right.GetCount(); i++)
159 {
160 if (m_right[i]->anchor == obj)
161 {
162 m_right.RemoveAt(i);
163 return true;
164 }
165 }
166 return false;
167}
168
169// Do we have this float already?
170bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
171{
172 size_t i;
173 for (i = 0; i < m_left.GetCount(); i++)
174 {
175 if (m_left[i]->anchor == obj)
176 {
177 return true;
178 }
179 }
180 for (i = 0; i < m_right.GetCount(); i++)
181 {
182 if (m_right[i]->anchor == obj)
183 {
184 return true;
185 }
186 }
187 return false;
188}
189
603f702b
JS
190// Get floating objects
191bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
192{
193 size_t i;
194 for (i = 0; i < m_left.GetCount(); i++)
195 objects.Append(m_left[i]->anchor);
196 for (i = 0; i < m_right.GetCount(); i++)
197 objects.Append(m_right[i]->anchor);
198 return true;
199}
200
201
cdaed652
VZ
202/*
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
207 */
208int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
209{
210 int end = array.GetCount() - 1;
211 int start = 0;
212 int ret = 0;
213
214 wxASSERT(end >= 0);
215
216 while (true)
217 {
218 if (start > end)
219 {
220 break;
221 }
222
223 int mid = (start + end) / 2;
224 if (array[mid]->startY <= point && array[mid]->endY >= point)
225 return mid;
226 else if (array[mid]->startY > point)
227 {
228 end = mid - 1;
229 ret = mid;
230 }
231 else if (array[mid]->endY < point)
232 {
233 start = mid + 1;
234 ret = start;
235 }
236 }
237
238 return ret;
239}
240
241int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
242{
243 int ret = 0;
244 int len = array.GetCount();
245
246 wxASSERT(index >= 0 && index < len);
247
248 if (array[index]->startY < startY && array[index]->endY > startY)
249 ret = ret < array[index]->width ? array[index]->width : ret;
250 while (index < len && array[index]->startY <= endY)
251 {
252 ret = ret < array[index]->width ? array[index]->width : ret;
253 index++;
254 }
255
256 return ret;
257}
258
603f702b 259wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
cdaed652 260{
603f702b 261 m_availableRect = rect;
cdaed652
VZ
262 m_para = NULL;
263}
264
265void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
266{
267 int len = array.GetCount();
268 for (int i = 0; i < len; i++)
269 delete array[i];
270}
271
272wxRichTextFloatCollector::~wxRichTextFloatCollector()
273{
274 FreeFloatRectMapArray(m_left);
275 FreeFloatRectMapArray(m_right);
276}
277
278int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
279{
280 if (array.GetCount() == 0)
281 return start;
282
603f702b 283 int i = SearchAdjacentRect(array, start);
cdaed652 284 int last = start;
603f702b 285 while (i < (int) array.GetCount())
cdaed652
VZ
286 {
287 if (array[i]->startY - last >= height)
288 return last + 1;
289 last = array[i]->endY;
290 i++;
291 }
292
293 return last + 1;
294}
295
296int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
297{
24777478 298 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
cdaed652 299 return GetFitPosition(m_left, start, height);
24777478 300 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
cdaed652
VZ
301 return GetFitPosition(m_right, start, height);
302 else
303 {
304 wxASSERT("Never should be here");
305 return start;
306 }
307}
308
603f702b
JS
309// Adds a floating image to the float collector.
310// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
cdaed652
VZ
311void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
312{
603f702b 313 int direction = floating->GetFloatDirection();
24777478 314
603f702b
JS
315 wxPoint pos = floating->GetPosition();
316 wxSize size = floating->GetCachedSize();
317 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
318 switch (direction)
319 {
320 case wxTEXT_BOX_ATTR_FLOAT_NONE:
321 delete map;
322 break;
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
326 m_left.Add(map);
327 break;
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
330 m_right.Add(map);
331 break;
332 default:
333 delete map;
334 wxASSERT("Unrecognised float attribute.");
335 }
cdaed652 336
603f702b 337 m_para = para;
cdaed652
VZ
338}
339
340void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
341{
342 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
343 while (node)
344 {
345 wxRichTextObject* floating = node->GetData();
ce00f59b 346
cdaed652
VZ
347 if (floating->IsFloating())
348 {
bec80f4f 349 CollectFloat(para, floating);
cdaed652 350 }
ce00f59b 351
cdaed652
VZ
352 node = node->GetNext();
353 }
354
355 m_para = para;
356}
357
358wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
359{
360 return m_para;
361}
362
363wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
364{
365 int widthLeft = 0, widthRight = 0;
366 if (m_left.GetCount() != 0)
367 {
603f702b
JS
368 int i = SearchAdjacentRect(m_left, startY);
369 if (i < (int) m_left.GetCount())
cdaed652
VZ
370 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
371 }
372 if (m_right.GetCount() != 0)
373 {
603f702b
JS
374 int j = SearchAdjacentRect(m_right, startY);
375 if (j < (int) m_right.GetCount())
cdaed652
VZ
376 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
377 }
378
603f702b
JS
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
cdaed652
VZ
383}
384
385int wxRichTextFloatCollector::GetLastRectBottom()
386{
387 int ret = 0;
388 int len = m_left.GetCount();
389 if (len) {
390 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
391 }
392 len = m_right.GetCount();
393 if (len) {
394 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
395 }
396
397 return ret;
398}
399
8db2e3ef 400void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
401{
402 int start = rect.y;
403 int end = rect.y + rect.height;
603f702b 404 int i, j;
cdaed652 405 i = SearchAdjacentRect(array, start);
603f702b 406 if (i < 0 || i >= (int) array.GetCount())
cdaed652
VZ
407 return;
408 j = SearchAdjacentRect(array, end);
603f702b 409 if (j < 0 || j >= (int) array.GetCount())
cdaed652
VZ
410 j = array.GetCount() - 1;
411 while (i <= j)
412 {
413 wxRichTextObject* obj = array[i]->anchor;
414 wxRichTextRange r = obj->GetRange();
8db2e3ef 415 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
cdaed652
VZ
416 i++;
417 }
418}
ecb5fbf1 419
8db2e3ef 420void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
421{
422 if (m_left.GetCount() > 0)
8db2e3ef 423 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
cdaed652 424 if (m_right.GetCount() > 0)
8db2e3ef 425 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
cdaed652
VZ
426}
427
8db2e3ef 428int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
cdaed652 429{
603f702b 430 int i;
cdaed652
VZ
431 if (array.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE;
433 i = SearchAdjacentRect(array, pt.y);
603f702b 434 if (i < 0 || i >= (int) array.GetCount())
cdaed652 435 return wxRICHTEXT_HITTEST_NONE;
603f702b
JS
436 if (!array[i]->anchor->IsShown())
437 return wxRICHTEXT_HITTEST_NONE;
438
cdaed652
VZ
439 wxPoint point = array[i]->anchor->GetPosition();
440 wxSize size = array[i]->anchor->GetCachedSize();
441 if (point.x <= pt.x && point.x + size.x >= pt.x
442 && point.y <= pt.y && point.y + size.y >= pt.y)
443 {
444 textPosition = array[i]->anchor->GetRange().GetStart();
603f702b 445 * obj = array[i]->anchor;
cdaed652
VZ
446 if (pt.x > (pt.x + pt.x + size.x) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE;
448 else
449 return wxRICHTEXT_HITTEST_AFTER;
450 }
ce00f59b 451
cdaed652
VZ
452 return wxRICHTEXT_HITTEST_NONE;
453}
454
8db2e3ef 455int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
cdaed652 456{
8db2e3ef 457 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
458 if (ret == wxRICHTEXT_HITTEST_NONE)
459 {
8db2e3ef 460 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
461 }
462 return ret;
463}
464
ce00f59b 465// Helpers for efficiency
ecb5fbf1
JS
466inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
467{
ecb5fbf1
JS
468 dc.SetFont(font);
469}
470
471inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
472{
473 const wxPen& pen1 = dc.GetPen();
474 if (pen1.IsOk() && pen.IsOk())
475 {
476 if (pen1.GetWidth() == pen.GetWidth() &&
477 pen1.GetStyle() == pen.GetStyle() &&
478 pen1.GetColour() == pen.GetColour())
479 return;
480 }
481 dc.SetPen(pen);
482}
483
484inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
485{
486 const wxBrush& brush1 = dc.GetBrush();
487 if (brush1.IsOk() && brush.IsOk())
488 {
489 if (brush1.GetStyle() == brush.GetStyle() &&
490 brush1.GetColour() == brush.GetColour())
491 return;
492 }
493 dc.SetBrush(brush);
494}
495
5d7836c4
JS
496/*!
497 * wxRichTextObject
498 * This is the base for drawable objects.
499 */
500
501IMPLEMENT_CLASS(wxRichTextObject, wxObject)
502
503wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
504{
5d7836c4
JS
505 m_refCount = 1;
506 m_parent = parent;
5d7836c4 507 m_descent = 0;
603f702b 508 m_show = true;
5d7836c4
JS
509}
510
511wxRichTextObject::~wxRichTextObject()
512{
513}
514
515void wxRichTextObject::Dereference()
516{
517 m_refCount --;
518 if (m_refCount <= 0)
519 delete this;
520}
521
522/// Copy
523void wxRichTextObject::Copy(const wxRichTextObject& obj)
524{
525 m_size = obj.m_size;
603f702b
JS
526 m_maxSize = obj.m_maxSize;
527 m_minSize = obj.m_minSize;
5d7836c4 528 m_pos = obj.m_pos;
5d7836c4 529 m_range = obj.m_range;
603f702b 530 m_ownRange = obj.m_ownRange;
5d7836c4 531 m_attributes = obj.m_attributes;
bec80f4f 532 m_properties = obj.m_properties;
5d7836c4 533 m_descent = obj.m_descent;
603f702b
JS
534 m_show = obj.m_show;
535}
536
537// Get/set the top-level container of this object.
538wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
539{
540 const wxRichTextObject* p = this;
541 while (p)
542 {
543 if (p->IsTopLevel())
544 {
545 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
546 }
547 p = p->GetParent();
548 }
549 return NULL;
5d7836c4
JS
550}
551
552void wxRichTextObject::SetMargins(int margin)
553{
603f702b 554 SetMargins(margin, margin, margin, margin);
5d7836c4
JS
555}
556
557void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
558{
603f702b
JS
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
563}
564
565int wxRichTextObject::GetLeftMargin() const
566{
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
568}
569
570int wxRichTextObject::GetRightMargin() const
571{
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
573}
574
575int wxRichTextObject::GetTopMargin() const
576{
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
578}
579
580int wxRichTextObject::GetBottomMargin() const
581{
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
583}
584
585// Calculate the available content space in the given rectangle, given the
586// margins, border and padding specified in the object's attributes.
8db2e3ef 587wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
603f702b
JS
588{
589 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590 marginRect = outerRect;
8db2e3ef
JS
591 wxRichTextAttr attr(GetAttributes());
592 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
594 return contentRect;
595}
596
597// Invalidate the buffer. With no argument, invalidates whole buffer.
598void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
599{
600 if (invalidRange != wxRICHTEXT_NONE)
601 {
23698b12
JS
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating())
606 SetCachedSize(wxDefaultSize);
603f702b
JS
607 SetMaxSize(wxDefaultSize);
608 SetMinSize(wxDefaultSize);
609 }
5d7836c4
JS
610}
611
44219ff0 612// Convert units in tenths of a millimetre to device units
cdaed652 613int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
5d7836c4 614{
44219ff0 615 // Unscale
bec80f4f
JS
616 double scale = 1.0;
617 if (GetBuffer())
32423dd8 618 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
bec80f4f
JS
619 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
620
44219ff0
JS
621 return p;
622}
623
624// Convert units in tenths of a millimetre to device units
bec80f4f 625int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
44219ff0 626{
5d7836c4
JS
627 // There are ppi pixels in 254.1 "1/10 mm"
628
629 double pixels = ((double) units * (double)ppi) / 254.1;
bec80f4f
JS
630 if (scale != 1.0)
631 pixels /= scale;
5d7836c4 632
603f702b
JS
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels == 0 && units > 0)
635 pixels = 1;
636
5d7836c4
JS
637 return (int) pixels;
638}
639
24777478
JS
640// Convert units in pixels to tenths of a millimetre
641int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
642{
643 int p = pixels;
bec80f4f
JS
644 double scale = 1.0;
645 if (GetBuffer())
646 scale = GetBuffer()->GetScale();
647
648 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
24777478
JS
649}
650
bec80f4f 651int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
24777478
JS
652{
653 // There are ppi pixels in 254.1 "1/10 mm"
bec80f4f
JS
654
655 double p = double(pixels);
656
657 if (scale != 1.0)
658 p *= scale;
659
660 int units = int( p * 254.1 / (double) ppi );
24777478
JS
661 return units;
662}
663
bec80f4f 664// Draw the borders and background for the given rectangle and attributes.
603f702b
JS
665// Width and height are taken to be the outer margin size, not the content.
666bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
bec80f4f
JS
667{
668 // Assume boxRect is the area around the content
603f702b
JS
669 wxRect marginRect = boxRect;
670 wxRect contentRect, borderRect, paddingRect, outlineRect;
bec80f4f 671
603f702b 672 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
bec80f4f
JS
673
674 // Margin is transparent. Draw background from margin.
603f702b 675 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
bec80f4f 676 {
603f702b
JS
677 wxColour colour;
678 if (flags & wxRICHTEXT_DRAW_SELECTED)
679 {
680 // TODO: get selection colour from control?
681 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
682 }
683 else
684 colour = attr.GetBackgroundColour();
685
686 wxPen pen(colour);
687 wxBrush brush(colour);
bec80f4f
JS
688
689 dc.SetPen(pen);
690 dc.SetBrush(brush);
37e7b783 691 dc.DrawRectangle(borderRect);
bec80f4f
JS
692 }
693
603f702b
JS
694 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
695 {
696 wxRichTextAttr editBorderAttr = attr;
697 // TODO: make guideline colour configurable
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
701
702 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
703 }
704
705 if (attr.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
bec80f4f 707
603f702b
JS
708 if (attr.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
bec80f4f
JS
710
711 return true;
712}
713
714// Draw a border
603f702b 715bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
bec80f4f
JS
716{
717 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
603f702b 718 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 719
603f702b 720 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
721 {
722 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723 wxColour col(attr.GetLeft().GetColour());
724
725 // If pen width is > 1, resorts to a solid rectangle.
726 if (borderLeft == 1)
727 {
728 int penStyle = wxSOLID;
729 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
730 penStyle = wxDOT;
731 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732 penStyle = wxLONG_DASH;
603f702b 733 wxPen pen(col, 1, penStyle);
bec80f4f
JS
734 dc.SetPen(pen);
735 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
736
737 }
738 else if (borderLeft > 1)
739 {
740 wxPen pen(col);
741 wxBrush brush(col);
742 dc.SetPen(pen);
743 dc.SetBrush(brush);
744 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
745 }
746 }
747
603f702b 748 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
749 {
750 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
751
752 wxColour col(attr.GetRight().GetColour());
753
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight == 1)
756 {
757 int penStyle = wxSOLID;
758 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
759 penStyle = wxDOT;
760 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761 penStyle = wxLONG_DASH;
603f702b 762 wxPen pen(col, 1, penStyle);
bec80f4f 763 dc.SetPen(pen);
603f702b 764 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
bec80f4f
JS
765
766 }
767 else if (borderRight > 1)
768 {
769 wxPen pen(col);
770 wxBrush brush(col);
771 dc.SetPen(pen);
772 dc.SetBrush(brush);
63af79de 773 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
bec80f4f
JS
774 }
775 }
776
603f702b 777 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
778 {
779 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
780
781 wxColour col(attr.GetTop().GetColour());
782
783 // If pen width is > 1, resorts to a solid rectangle.
784 if (borderTop == 1)
785 {
786 int penStyle = wxSOLID;
787 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
788 penStyle = wxDOT;
789 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790 penStyle = wxLONG_DASH;
603f702b 791 wxPen pen(col, 1, penStyle);
bec80f4f
JS
792 dc.SetPen(pen);
793 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
794
795 }
796 else if (borderTop > 1)
797 {
798 wxPen pen(col);
799 wxBrush brush(col);
800 dc.SetPen(pen);
801 dc.SetBrush(brush);
802 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
803 }
804 }
805
603f702b 806 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
807 {
808 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
809 wxColour col(attr.GetTop().GetColour());
810
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom == 1)
813 {
814 int penStyle = wxSOLID;
815 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
816 penStyle = wxDOT;
817 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818 penStyle = wxLONG_DASH;
603f702b 819 wxPen pen(col, 1, penStyle);
bec80f4f
JS
820 dc.SetPen(pen);
821 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
822
823 }
824 else if (borderBottom > 1)
825 {
826 wxPen pen(col);
827 wxBrush brush(col);
828 dc.SetPen(pen);
829 dc.SetBrush(brush);
63af79de 830 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
bec80f4f
JS
831 }
832 }
833
834 return true;
835}
836
837// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838// or marginRect (outer), and the other must be the default rectangle (no width or height).
839// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
840// is available.
841//
842// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843
603f702b 844bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
bec80f4f
JS
845{
846 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
850
603f702b 851 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 852
603f702b 853 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f 854 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
603f702b 855 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
bec80f4f 856 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
603f702b 857 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
bec80f4f 858 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
83c6ae8e 859 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
bec80f4f
JS
860 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
861
603f702b 862 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f 863 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
603f702b 864 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
bec80f4f 865 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
603f702b 866 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
bec80f4f 867 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
83c6ae8e 868 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
bec80f4f
JS
869 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870
603f702b 871 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
bec80f4f 872 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
603f702b 873 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
bec80f4f 874 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
603f702b 875 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
bec80f4f 876 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b 877 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
bec80f4f
JS
878 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
879
603f702b 880 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
bec80f4f 881 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
603f702b 882 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
bec80f4f 883 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
603f702b 884 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
bec80f4f 885 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
603f702b 886 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
bec80f4f
JS
887 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888
889 int leftTotal = marginLeft + borderLeft + paddingLeft;
890 int rightTotal = marginRight + borderRight + paddingRight;
891 int topTotal = marginTop + borderTop + paddingTop;
892 int bottomTotal = marginBottom + borderBottom + paddingBottom;
893
894 if (marginRect != wxRect())
895 {
896 contentRect.x = marginRect.x + leftTotal;
897 contentRect.y = marginRect.y + topTotal;
898 contentRect.width = marginRect.width - (leftTotal + rightTotal);
899 contentRect.height = marginRect.height - (topTotal + bottomTotal);
900 }
901 else
902 {
903 marginRect.x = contentRect.x - leftTotal;
904 marginRect.y = contentRect.y - topTotal;
905 marginRect.width = contentRect.width + (leftTotal + rightTotal);
906 marginRect.height = contentRect.height + (topTotal + bottomTotal);
907 }
908
909 borderRect.x = marginRect.x + marginLeft;
910 borderRect.y = marginRect.y + marginTop;
911 borderRect.width = marginRect.width - (marginLeft + marginRight);
912 borderRect.height = marginRect.height - (marginTop + marginBottom);
913
914 paddingRect.x = marginRect.x + marginLeft + borderLeft;
915 paddingRect.y = marginRect.y + marginTop + borderTop;
916 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
918
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect.x = marginRect.x - outlineLeft;
921 outlineRect.y = marginRect.y - outlineTop;
922 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
924
925 return true;
926}
927
603f702b
JS
928// Get the total margin for the object in pixels, taking into account margin, padding and border size
929bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930 int& topMargin, int& bottomMargin)
931{
932 // Assume boxRect is the area around the content
933 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934 marginRect = wxRect(0, 0, 1000, 1000);
bec80f4f 935
603f702b
JS
936 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
937
938 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939 rightMargin = marginRect.GetRight() - contentRect.GetRight();
940 topMargin = contentRect.GetTop() - marginRect.GetTop();
941 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
942
943 return true;
944}
945
946// Returns the rectangle which the child has available to it given restrictions specified in the
947// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
bb7bbd12
JS
948// availableContainerSpace might be a parent that the cell has to compute its width relative to.
949// E.g. a cell that's 50% of its parent.
950wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
603f702b
JS
951{
952 wxRect rect = availableParentSpace;
953 double scale = 1.0;
954 if (buffer)
955 scale = buffer->GetScale();
956
bb7bbd12 957 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
603f702b
JS
958
959 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
961
962 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
964
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 {
969 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
970 }
971 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 {
973 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 975 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
603f702b
JS
976 else
977 rect.x += x;
978 }
979
980 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 {
982 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
983 }
984 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 {
986 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 988 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
603f702b
JS
989 else
990 rect.y += y;
991 }
992
bb7bbd12
JS
993 if (rect.GetWidth() > availableParentSpace.GetWidth())
994 rect.SetWidth(availableParentSpace.GetWidth());
995
603f702b
JS
996 return rect;
997}
998
999// Dump to output stream for debugging
5d7836c4
JS
1000void wxRichTextObject::Dump(wxTextOutputStream& stream)
1001{
1002 stream << GetClassInfo()->GetClassName() << wxT("\n");
1003 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");
1004 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");
1005}
1006
603f702b 1007// Gets the containing buffer
44219ff0
JS
1008wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1009{
1010 const wxRichTextObject* obj = this;
345c78ca 1011 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
44219ff0
JS
1012 obj = obj->GetParent();
1013 return wxDynamicCast(obj, wxRichTextBuffer);
1014}
5d7836c4 1015
603f702b
JS
1016// Get the absolute object position, by traversing up the child/parent hierarchy
1017wxPoint wxRichTextObject::GetAbsolutePosition() const
1018{
1019 wxPoint pt = GetPosition();
1020
1021 wxRichTextObject* p = GetParent();
1022 while (p)
1023 {
1024 pt = pt + p->GetPosition();
1025 p = p->GetParent();
1026 }
1027
1028 return pt;
1029}
1030
1031// Hit-testing: returns a flag indicating hit test details, plus
1032// information about position
8db2e3ef 1033int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
603f702b
JS
1034{
1035 if (!IsShown())
1036 return wxRICHTEXT_HITTEST_NONE;
1037
1038 wxRect rect = GetRect();
1039 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040 pt.y >= rect.y && pt.y < rect.y + rect.height)
1041 {
1042 *obj = this;
1043 *contextObj = GetParentContainer();
1044 textPosition = GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON;
1046 }
1047 else
1048 return wxRICHTEXT_HITTEST_NONE;
1049}
1050
1051// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052// lays out the object again using the maximum ('best') size
8db2e3ef 1053bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
bb7bbd12
JS
1054 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
603f702b
JS
1056 int style)
1057{
bb7bbd12 1058 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
603f702b 1059 wxRect originalAvailableRect = availableChildRect;
8db2e3ef 1060 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1061
1062 wxSize maxSize = GetMaxSize();
1063
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 // on this basis
bb7bbd12 1066 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
603f702b
JS
1067 {
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL);
1070 wxRichTextAttr newAttr(attr);
1071 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1073
bb7bbd12 1074 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
603f702b
JS
1075
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
07d4142f 1079 if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
603f702b
JS
1080 {
1081 // centering, right-justification
8db2e3ef 1082 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
1083 {
1084 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1085 }
8db2e3ef 1086 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
1087 {
1088 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1089 }
1090 }
1091
8db2e3ef 1092 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1093 }
1094
1095 /*
1096 __________________
1097 | ____________ |
1098 | | | |
1099
1100
1101 */
1102
1103 return true;
1104}
1105
1106// Move the object recursively, by adding the offset from old to new
1107void wxRichTextObject::Move(const wxPoint& pt)
1108{
1109 SetPosition(pt);
1110}
1111
1112
5d7836c4
JS
1113/*!
1114 * wxRichTextCompositeObject
1115 * This is the base for drawable objects.
1116 */
1117
1118IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1119
1120wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1121 wxRichTextObject(parent)
1122{
1123}
1124
1125wxRichTextCompositeObject::~wxRichTextCompositeObject()
1126{
1127 DeleteChildren();
1128}
1129
1130/// Get the nth child
1131wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1132{
1133 wxASSERT ( n < m_children.GetCount() );
1134
1135 return m_children.Item(n)->GetData();
1136}
1137
1138/// Append a child, returning the position
1139size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1140{
1141 m_children.Append(child);
1142 child->SetParent(this);
1143 return m_children.GetCount() - 1;
1144}
1145
1146/// Insert the child in front of the given object, or at the beginning
1147bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1148{
1149 if (inFrontOf)
1150 {
1151 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1152 m_children.Insert(node, child);
1153 }
1154 else
1155 m_children.Insert(child);
1156 child->SetParent(this);
1157
1158 return true;
1159}
1160
1161/// Delete the child
1162bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1163{
1164 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1165 if (node)
1166 {
efbf6735
JS
1167 wxRichTextObject* obj = node->GetData();
1168 m_children.Erase(node);
5d7836c4 1169 if (deleteChild)
efbf6735 1170 delete obj;
5d7836c4
JS
1171
1172 return true;
1173 }
1174 return false;
1175}
1176
1177/// Delete all children
1178bool wxRichTextCompositeObject::DeleteChildren()
1179{
1180 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1181 while (node)
1182 {
1183 wxRichTextObjectList::compatibility_iterator oldNode = node;
1184
1185 wxRichTextObject* child = node->GetData();
1186 child->Dereference(); // Only delete if reference count is zero
1187
1188 node = node->GetNext();
efbf6735 1189 m_children.Erase(oldNode);
5d7836c4
JS
1190 }
1191
1192 return true;
1193}
1194
1195/// Get the child count
1196size_t wxRichTextCompositeObject::GetChildCount() const
1197{
1198 return m_children.GetCount();
1199}
1200
1201/// Copy
1202void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1203{
1204 wxRichTextObject::Copy(obj);
1205
1206 DeleteChildren();
1207
1208 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1209 while (node)
1210 {
1211 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
1212 wxRichTextObject* newChild = child->Clone();
1213 newChild->SetParent(this);
1214 m_children.Append(newChild);
5d7836c4
JS
1215
1216 node = node->GetNext();
1217 }
1218}
1219
1220/// Hit-testing: returns a flag indicating hit test details, plus
1221/// information about position
8db2e3ef 1222int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 1223{
603f702b
JS
1224 if (!IsShown())
1225 return wxRICHTEXT_HITTEST_NONE;
1226
5d7836c4
JS
1227 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1228 while (node)
1229 {
1230 wxRichTextObject* child = node->GetData();
1231
603f702b
JS
1232 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1233 {
1234 // Just check if we hit the overall object
8db2e3ef 1235 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1236 if (ret != wxRICHTEXT_HITTEST_NONE)
1237 return ret;
1238 }
1239 else if (child->IsShown())
1240 {
8db2e3ef 1241 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1242 if (ret != wxRICHTEXT_HITTEST_NONE)
1243 return ret;
1244 }
5d7836c4
JS
1245
1246 node = node->GetNext();
1247 }
1248
603f702b 1249 return wxRICHTEXT_HITTEST_NONE;
5d7836c4
JS
1250}
1251
1252/// Finds the absolute position and row height for the given character position
8db2e3ef 1253bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
1254{
1255 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1256 while (node)
1257 {
1258 wxRichTextObject* child = node->GetData();
1259
603f702b
JS
1260 // Don't recurse if the child is a top-level object,
1261 // such as a text box, because the character position will no longer
1262 // apply. By definition, a top-level object has its own range of
1263 // character positions.
8db2e3ef 1264 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
5d7836c4
JS
1265 return true;
1266
1267 node = node->GetNext();
1268 }
1269
1270 return false;
1271}
1272
1273/// Calculate range
1274void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1275{
1276 long current = start;
1277 long lastEnd = current;
1278
603f702b
JS
1279 if (IsTopLevel())
1280 {
1281 current = 0;
1282 lastEnd = 0;
1283 }
1284
5d7836c4
JS
1285 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1286 while (node)
1287 {
1288 wxRichTextObject* child = node->GetData();
1289 long childEnd = 0;
1290
1291 child->CalculateRange(current, childEnd);
1292 lastEnd = childEnd;
1293
1294 current = childEnd + 1;
1295
1296 node = node->GetNext();
1297 }
1298
603f702b
JS
1299 if (IsTopLevel())
1300 {
1301 // A top-level object always has a range of size 1,
1302 // because its children don't count at this level.
1303 end = start;
1304 m_range.SetRange(start, start);
5d7836c4 1305
603f702b
JS
1306 // An object with no children has zero length
1307 if (m_children.GetCount() == 0)
1308 lastEnd --;
1309 m_ownRange.SetRange(0, lastEnd);
1310 }
1311 else
1312 {
1313 end = lastEnd;
5d7836c4 1314
603f702b
JS
1315 // An object with no children has zero length
1316 if (m_children.GetCount() == 0)
1317 end --;
1318
1319 m_range.SetRange(start, end);
1320 }
5d7836c4
JS
1321}
1322
1323/// Delete range from layout.
1324bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1325{
1326 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1327
5d7836c4
JS
1328 while (node)
1329 {
1330 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1331 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1332
5d7836c4
JS
1333 // Delete the range in each paragraph
1334
1335 // When a chunk has been deleted, internally the content does not
1336 // now match the ranges.
1337 // However, so long as deletion is not done on the same object twice this is OK.
1338 // If you may delete content from the same object twice, recalculate
1339 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1340 // adjust the range you're deleting accordingly.
7fe8059f 1341
5d7836c4
JS
1342 if (!obj->GetRange().IsOutside(range))
1343 {
603f702b
JS
1344 // No need to delete within a top-level object; just removing this object will do fine
1345 if (!obj->IsTopLevel())
1346 obj->DeleteRange(range);
5d7836c4
JS
1347
1348 // Delete an empty object, or paragraph within this range.
1349 if (obj->IsEmpty() ||
1350 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1351 {
1352 // An empty paragraph has length 1, so won't be deleted unless the
1353 // whole range is deleted.
7fe8059f 1354 RemoveChild(obj, true);
5d7836c4
JS
1355 }
1356 }
7fe8059f 1357
5d7836c4
JS
1358 node = next;
1359 }
7fe8059f 1360
5d7836c4
JS
1361 return true;
1362}
1363
1364/// Get any text in this object for the given range
1365wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1366{
1367 wxString text;
1368 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1369 while (node)
1370 {
1371 wxRichTextObject* child = node->GetData();
1372 wxRichTextRange childRange = range;
1373 if (!child->GetRange().IsOutside(range))
1374 {
1375 childRange.LimitTo(child->GetRange());
7fe8059f 1376
5d7836c4 1377 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1378
5d7836c4
JS
1379 text += childText;
1380 }
1381 node = node->GetNext();
1382 }
1383
1384 return text;
1385}
1386
603f702b
JS
1387/// Get the child object at the given character position
1388wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1389{
1390 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1391 while (node)
1392 {
1393 wxRichTextObject* child = node->GetData();
1394 if (child->GetRange().GetStart() == pos)
1395 return child;
1396 node = node->GetNext();
1397 }
1398 return NULL;
1399}
1400
5d7836c4 1401/// Recursively merge all pieces that can be merged.
109bfc88 1402bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
5d7836c4
JS
1403{
1404 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1405 while (node)
1406 {
1407 wxRichTextObject* child = node->GetData();
5cb0b827 1408 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
5d7836c4 1409 {
109bfc88
JS
1410 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1411 if (composite)
1412 composite->Defragment();
1413
1414 if (node->GetNext())
5d7836c4 1415 {
109bfc88
JS
1416 wxRichTextObject* nextChild = node->GetNext()->GetData();
1417 if (child->CanMerge(nextChild) && child->Merge(nextChild))
1418 {
1419 nextChild->Dereference();
1420 m_children.Erase(node->GetNext());
5d7836c4 1421
109bfc88
JS
1422 // Don't set node -- we'll see if we can merge again with the next
1423 // child.
1424 }
1425 else
1426 node = node->GetNext();
5d7836c4
JS
1427 }
1428 else
1429 node = node->GetNext();
1430 }
1431 else
1432 node = node->GetNext();
1433 }
1434
bec80f4f
JS
1435 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1436 if (GetChildCount() > 1)
5d7836c4 1437 {
bec80f4f
JS
1438 node = m_children.GetFirst();
1439 while (node)
1440 {
1441 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1442 wxRichTextObject* child = node->GetData();
1443 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1444 {
1445 if (child->IsEmpty())
1446 {
1447 child->Dereference();
1448 m_children.Erase(node);
1449 }
1450 node = next;
1451 }
1452 else
1453 node = node->GetNext();
1454 }
5d7836c4 1455 }
5d7836c4 1456
5d7836c4
JS
1457 return true;
1458}
1459
bec80f4f
JS
1460/// Dump to output stream for debugging
1461void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1462{
1463 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1464 while (node)
1465 {
1466 wxRichTextObject* child = node->GetData();
bec80f4f 1467 child->Dump(stream);
5d7836c4
JS
1468 node = node->GetNext();
1469 }
5d7836c4
JS
1470}
1471
603f702b
JS
1472/// Get/set the object size for the given range. Returns false if the range
1473/// is invalid for this object.
8db2e3ef 1474bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b
JS
1475{
1476 if (!range.IsWithin(GetRange()))
1477 return false;
5d7836c4 1478
603f702b 1479 wxSize sz;
5d7836c4 1480
603f702b
JS
1481 wxArrayInt childExtents;
1482 wxArrayInt* p;
1483 if (partialExtents)
1484 p = & childExtents;
1485 else
1486 p = NULL;
5d7836c4 1487
603f702b
JS
1488 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1489 while (node)
cdaed652 1490 {
603f702b
JS
1491 wxRichTextObject* child = node->GetData();
1492 if (!child->GetRange().IsOutside(range))
1493 {
1494 // Floating objects have a zero size within the paragraph.
1495 if (child->IsFloating())
1496 {
1497 if (partialExtents)
1498 {
1499 int lastSize;
1500 if (partialExtents->GetCount() > 0)
1501 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1502 else
1503 lastSize = 0;
cdaed652 1504
603f702b
JS
1505 partialExtents->Add(0 /* zero size */ + lastSize);
1506 }
1507 }
1508 else
1509 {
1510 wxSize childSize;
5d7836c4 1511
603f702b
JS
1512 wxRichTextRange rangeToUse = range;
1513 rangeToUse.LimitTo(child->GetRange());
1514 if (child->IsTopLevel())
1515 rangeToUse = child->GetOwnRange();
5d7836c4 1516
603f702b 1517 int childDescent = 0;
cdaed652 1518
603f702b
JS
1519 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1520 // but it's only going to be used after caching has taken place.
1521 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1522 {
1523 childDescent = child->GetDescent();
1524 childSize = child->GetCachedSize();
bec80f4f 1525
603f702b
JS
1526 sz.y = wxMax(sz.y, childSize.y);
1527 sz.x += childSize.x;
1528 descent = wxMax(descent, childDescent);
1529 }
8db2e3ef 1530 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b
JS
1531 {
1532 sz.y = wxMax(sz.y, childSize.y);
1533 sz.x += childSize.x;
1534 descent = wxMax(descent, childDescent);
bec80f4f 1535
603f702b
JS
1536 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1537 {
1538 child->SetCachedSize(childSize);
1539 child->SetDescent(childDescent);
1540 }
bec80f4f 1541
603f702b
JS
1542 if (partialExtents)
1543 {
1544 int lastSize;
1545 if (partialExtents->GetCount() > 0)
1546 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1547 else
1548 lastSize = 0;
bec80f4f 1549
603f702b
JS
1550 size_t i;
1551 for (i = 0; i < childExtents.GetCount(); i++)
1552 {
1553 partialExtents->Add(childExtents[i] + lastSize);
1554 }
1555 }
1556 }
1557 }
1558
1559 if (p)
1560 p->Clear();
1561 }
1562
1563 node = node->GetNext();
1564 }
1565 size = sz;
1566 return true;
1567}
1568
1569// Invalidate the buffer. With no argument, invalidates whole buffer.
1570void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1571{
1572 wxRichTextObject::Invalidate(invalidRange);
1573
1574 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1575 while (node)
1576 {
1577 wxRichTextObject* child = node->GetData();
1578 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1579 {
1580 // Skip
1581 }
1582 else if (child->IsTopLevel())
1583 {
1584 if (invalidRange == wxRICHTEXT_NONE)
1585 child->Invalidate(wxRICHTEXT_NONE);
1586 else
1587 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1588 }
1589 else
1590 child->Invalidate(invalidRange);
1591 node = node->GetNext();
1592 }
1593}
1594
1595// Move the object recursively, by adding the offset from old to new
1596void wxRichTextCompositeObject::Move(const wxPoint& pt)
1597{
1598 wxPoint oldPos = GetPosition();
1599 SetPosition(pt);
1600 wxPoint offset = pt - oldPos;
1601
1602 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1603 while (node)
1604 {
1605 wxRichTextObject* child = node->GetData();
1606 wxPoint childPos = child->GetPosition() + offset;
1607 child->Move(childPos);
1608 node = node->GetNext();
1609 }
1610}
1611
1612
1613/*!
1614 * wxRichTextParagraphLayoutBox
1615 * This box knows how to lay out paragraphs.
1616 */
1617
1618IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1619
1620wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1621 wxRichTextCompositeObject(parent)
1622{
1623 Init();
1624}
1625
1626wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1627{
1628 if (m_floatCollector)
1629 {
1630 delete m_floatCollector;
1631 m_floatCollector = NULL;
1632 }
1633}
1634
1635/// Initialize the object.
1636void wxRichTextParagraphLayoutBox::Init()
1637{
1638 m_ctrl = NULL;
1639
1640 // For now, assume is the only box and has no initial size.
1641 m_range = wxRichTextRange(0, -1);
1642 m_ownRange = wxRichTextRange(0, -1);
1643
1644 m_invalidRange = wxRICHTEXT_ALL;
1645
603f702b
JS
1646 m_partialParagraph = false;
1647 m_floatCollector = NULL;
1648}
1649
1650void wxRichTextParagraphLayoutBox::Clear()
1651{
1652 DeleteChildren();
1653
1654 if (m_floatCollector)
1655 delete m_floatCollector;
1656 m_floatCollector = NULL;
1657 m_partialParagraph = false;
1658}
1659
1660/// Copy
1661void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1662{
1663 Clear();
1664
1665 wxRichTextCompositeObject::Copy(obj);
1666
1667 m_partialParagraph = obj.m_partialParagraph;
1668 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1669}
1670
07d4142f
JS
1671// Gather information about floating objects; only gather floats for those paragraphs that
1672// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1673// during layout.
603f702b 1674bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1675{
1676 if (m_floatCollector != NULL)
1677 delete m_floatCollector;
603f702b 1678 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652 1679 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
07d4142f
JS
1680 // Only gather floats up to the point we'll start formatting paragraphs.
1681 while (untilObj && node && node->GetData() != untilObj)
cdaed652
VZ
1682 {
1683 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1684 wxASSERT (child != NULL);
1685 if (child)
1686 m_floatCollector->CollectFloat(child);
1687 node = node->GetNext();
1688 }
ce00f59b 1689
cdaed652
VZ
1690 return true;
1691}
1692
603f702b
JS
1693// Returns the style sheet associated with the overall buffer.
1694wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1695{
1696 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1697}
1698
1699// Get the number of floating objects at this level
1700int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1701{
1702 if (m_floatCollector)
1703 return m_floatCollector->GetFloatingObjectCount();
1704 else
1705 return 0;
1706}
1707
1708// Get a list of floating objects
1709bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1710{
1711 if (m_floatCollector)
1712 {
1713 return m_floatCollector->GetFloatingObjects(objects);
1714 }
1715 else
1716 return false;
1717}
1718
1719// Calculate ranges
1720void wxRichTextParagraphLayoutBox::UpdateRanges()
1721{
1722 long start = 0;
1723 if (GetParent())
1724 start = GetRange().GetStart();
1725 long end;
1726 CalculateRange(start, end);
1727}
1728
cdaed652 1729// HitTest
8db2e3ef 1730int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1731{
603f702b
JS
1732 if (!IsShown())
1733 return wxRICHTEXT_HITTEST_NONE;
1734
cdaed652 1735 int ret = wxRICHTEXT_HITTEST_NONE;
343ef639 1736 if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
8db2e3ef 1737 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
ce00f59b 1738
cdaed652 1739 if (ret == wxRICHTEXT_HITTEST_NONE)
8db2e3ef 1740 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
cdaed652 1741 else
603f702b
JS
1742 {
1743 *contextObj = this;
cdaed652 1744 return ret;
603f702b 1745 }
cdaed652
VZ
1746}
1747
1748/// Draw the floating objects
8db2e3ef 1749void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
1750{
1751 if (m_floatCollector)
8db2e3ef 1752 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
cdaed652
VZ
1753}
1754
bec80f4f 1755void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1756{
1757 if (from == to)
1758 return;
1759
1760 from->RemoveChild(obj);
1761 to->AppendChild(obj);
5d7836c4
JS
1762}
1763
1764/// Draw the item
8db2e3ef 1765bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1766{
603f702b
JS
1767 if (!IsShown())
1768 return true;
1769
1770 wxRect thisRect(GetPosition(), GetCachedSize());
1771
8db2e3ef
JS
1772 wxRichTextAttr attr(GetAttributes());
1773 context.ApplyVirtualAttributes(attr, this);
1774
603f702b
JS
1775 int flags = style;
1776 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1777 flags |= wxRICHTEXT_DRAW_SELECTED;
1778
1779 // Don't draw guidelines if at top level
1780 int theseFlags = flags;
1781 if (!GetParent())
1782 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
8db2e3ef 1783 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
603f702b 1784
8db2e3ef 1785 DrawFloats(dc, context, range, selection, rect, descent, style);
5d7836c4
JS
1786 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1787 while (node)
1788 {
603f702b 1789 wxRichTextObject* child = node->GetData();
7fe8059f 1790
5d7836c4
JS
1791 if (child && !child->GetRange().IsOutside(range))
1792 {
1793 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1794 wxRichTextRange childRange = range;
1795 if (child->IsTopLevel())
1796 {
1797 childRange = child->GetOwnRange();
1798 }
7fe8059f 1799
ea160b2e
JS
1800 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1801 {
1802 // Stop drawing
1803 break;
1804 }
1805 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1806 {
1807 // Skip
1808 }
1809 else
8db2e3ef 1810 child->Draw(dc, context, childRange, selection, rect, descent, style);
5d7836c4
JS
1811 }
1812
1813 node = node->GetNext();
1814 }
1815 return true;
1816}
1817
1818/// Lay the item out
8db2e3ef 1819bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 1820{
603f702b
JS
1821 SetPosition(rect.GetPosition());
1822
1823 if (!IsShown())
1824 return true;
1825
4d551ad5
JS
1826 wxRect availableSpace;
1827 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1828
8db2e3ef
JS
1829 wxRichTextAttr attr(GetAttributes());
1830 context.ApplyVirtualAttributes(attr, this);
1831
4d551ad5 1832 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1833 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1834 // so that during a size, only the visible part will be relaid out, or
1835 // it would take too long causing flicker. As an approximation, we assume that
1836 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1837 if (formatRect)
1838 {
603f702b 1839 wxRect rect2(0, 0, rect.width, rect.height);
8db2e3ef 1840 availableSpace = GetAvailableContentArea(dc, context, rect2);
4d551ad5
JS
1841
1842 // Invalidate the part of the buffer from the first visible line
1843 // to the end. If other parts of the buffer are currently invalid,
1844 // then they too will be taken into account if they are above
1845 // the visible point.
1846 long startPos = 0;
1847 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1848 if (line)
1849 startPos = line->GetAbsoluteRange().GetStart();
1850
603f702b 1851 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1852 }
1853 else
603f702b 1854 {
8db2e3ef 1855 availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
1856 }
1857
d157d142
JS
1858 // Fix the width if we're at the top level
1859 if (!GetParent())
1860 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1861
603f702b 1862 int leftMargin, rightMargin, topMargin, bottomMargin;
8db2e3ef 1863 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
603f702b 1864 topMargin, bottomMargin);
5d7836c4
JS
1865
1866 int maxWidth = 0;
603f702b
JS
1867 int maxHeight = 0;
1868
1869 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1870 int maxMaxWidth = 0;
1871
1872 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1873 int maxMinWidth = 0;
1874
1875 // If we have vertical alignment, we must recalculate everything.
8db2e3ef
JS
1876 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1877 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1878
5d7836c4 1879 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1880
38113684 1881 bool layoutAll = true;
1e967276 1882
38113684
JS
1883 // Get invalid range, rounding to paragraph start/end.
1884 wxRichTextRange invalidRange = GetInvalidRange(true);
1885
4d551ad5 1886 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1887 return true;
1888
603f702b 1889 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1890 layoutAll = true;
38113684 1891 else // If we know what range is affected, start laying out from that point on.
603f702b 1892 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1893 {
38113684 1894 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1895 if (firstParagraph)
1896 {
1897 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1898 wxRichTextObjectList::compatibility_iterator previousNode;
1899 if ( firstNode )
1900 previousNode = firstNode->GetPrevious();
9b4af7b7 1901 if (firstNode)
2c375f42 1902 {
9b4af7b7
JS
1903 if (previousNode)
1904 {
1905 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1906 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1907 }
7fe8059f 1908
2c375f42
JS
1909 // Now we're going to start iterating from the first affected paragraph.
1910 node = firstNode;
1e967276
JS
1911
1912 layoutAll = false;
2c375f42
JS
1913 }
1914 }
1915 }
1916
07d4142f
JS
1917 // Gather information about only those floating objects that will not be formatted,
1918 // after which floats will be gathered per-paragraph during layout.
603f702b 1919 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1920
4d551ad5
JS
1921 // A way to force speedy rest-of-buffer layout (the 'else' below)
1922 bool forceQuickLayout = false;
39a1c2f2 1923
d3f6b1b5
JS
1924 // First get the size of the paragraphs we won't be laying out
1925 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1926 while (n && n != node)
1927 {
1928 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
1929 if (child)
1930 {
1931 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1932 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1933 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1934 }
1935 n = n->GetNext();
1936 }
1937
5d7836c4
JS
1938 while (node)
1939 {
1940 // Assume this box only contains paragraphs
1941
1942 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
706465df
JS
1943 // Unsure if this is needed
1944 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 1945
603f702b 1946 if (child && child->IsShown())
2c375f42 1947 {
603f702b
JS
1948 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1949 if ( !forceQuickLayout &&
1950 (layoutAll ||
1951 child->GetLines().IsEmpty() ||
1952 !child->GetRange().IsOutside(invalidRange)) )
1953 {
1954 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1955 // lays out the object again using the minimum size
8db2e3ef
JS
1956 child->LayoutToBestSize(dc, context, GetBuffer(),
1957 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
1958
1959 // Layout must set the cached size
1960 availableSpace.y += child->GetCachedSize().y;
1961 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1962 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1963 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1964
1965 // If we're just formatting the visible part of the buffer,
1966 // and we're now past the bottom of the window, and we don't have any
1967 // floating objects (since they may cause wrapping to change for the rest of the
1968 // the buffer), start quick layout.
1969 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
1970 forceQuickLayout = true;
1971 }
1972 else
1973 {
1974 // We're outside the immediately affected range, so now let's just
1975 // move everything up or down. This assumes that all the children have previously
1976 // been laid out and have wrapped line lists associated with them.
1977 // TODO: check all paragraphs before the affected range.
1978
1979 int inc = availableSpace.y - child->GetPosition().y;
1980
1981 while (node)
1982 {
1983 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1984 if (child)
1985 {
1986 if (child->GetLines().GetCount() == 0)
1987 {
1988 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1989 // lays out the object again using the minimum size
8db2e3ef
JS
1990 child->LayoutToBestSize(dc, context, GetBuffer(),
1991 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
1992
1993 //child->Layout(dc, availableChildRect, style);
1994 }
1995 else
1996 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 1997
603f702b
JS
1998 availableSpace.y += child->GetCachedSize().y;
1999 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2000 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2001 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2002 }
4d551ad5 2003
603f702b
JS
2004 node = node->GetNext();
2005 }
2006 break;
2007 }
2c375f42 2008 }
7fe8059f 2009
603f702b
JS
2010 node = node->GetNext();
2011 }
2012
2013 node = m_children.GetLast();
2014 if (node && node->GetData()->IsShown())
2015 {
2016 wxRichTextObject* child = node->GetData();
603f702b
JS
2017 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2018 }
2019 else
2020 maxHeight = 0; // topMargin + bottomMargin;
2021
23698b12
JS
2022 // Check the bottom edge of any floating object
2023 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2024 {
2025 int bottom = GetFloatCollector()->GetLastRectBottom();
2026 if (bottom > maxHeight)
2027 maxHeight = bottom;
2028 }
2029
8db2e3ef 2030 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
bb7bbd12 2031 {
8db2e3ef 2032 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
bb7bbd12
JS
2033 int w = r.GetWidth();
2034
2035 // Convert external to content rect
2036 w = w - leftMargin - rightMargin;
2037 maxWidth = wxMax(maxWidth, w);
2038 maxMaxWidth = wxMax(maxMaxWidth, w);
2039 }
32423dd8
JS
2040 else
2041 {
2042 // TODO: Make sure the layout box's position reflects
2043 // the position of the children, but without
2044 // breaking layout of a box within a paragraph.
2045 }
bb7bbd12 2046
603f702b
JS
2047 // TODO: (also in para layout) should set the
2048 // object's size to an absolute one if specified,
2049 // but if not specified, calculate it from content.
2050
2051 // We need to add back the margins etc.
2052 {
2053 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2054 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
8db2e3ef 2055 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2056 SetCachedSize(marginRect.GetSize());
2057 }
2058
2059 // The maximum size is the greatest of all maximum widths for all paragraphs.
2060 {
2061 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2062 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2063 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2064 SetMaxSize(marginRect.GetSize());
2065 }
2066
2067 // The minimum size is the greatest of all minimum widths for all paragraphs.
2068 {
2069 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2070 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2071 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2072 SetMinSize(marginRect.GetSize());
2073 }
2074
8db2e3ef
JS
2075 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2076 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
603f702b
JS
2077 {
2078 int yOffset = 0;
2079 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2080 if (leftOverSpace > 0)
2081 {
8db2e3ef 2082 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
603f702b
JS
2083 {
2084 yOffset = (leftOverSpace/2);
2085 }
8db2e3ef 2086 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
603f702b
JS
2087 {
2088 yOffset = leftOverSpace;
2089 }
2090 }
7fe8059f 2091
603f702b
JS
2092 // Move all the children to vertically align the content
2093 // This doesn't take into account floating objects, unfortunately.
2094 if (yOffset != 0)
2095 {
2096 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
2097 while (node)
2098 {
2099 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2100 if (child)
603f702b 2101 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
2102
2103 node = node->GetNext();
2c375f42 2104 }
2c375f42 2105 }
5d7836c4
JS
2106 }
2107
1e967276 2108 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2109
2110 return true;
2111}
2112
5d7836c4 2113/// Get/set the size for the given range.
8db2e3ef 2114bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
5d7836c4
JS
2115{
2116 wxSize sz;
2117
09f14108
JS
2118 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2119 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2120
2121 // First find the first paragraph whose starting position is within the range.
2122 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2123 while (node)
2124 {
2125 // child is a paragraph
2126 wxRichTextObject* child = node->GetData();
2127 const wxRichTextRange& r = child->GetRange();
2128
2129 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2130 {
2131 startPara = node;
2132 break;
2133 }
2134
2135 node = node->GetNext();
2136 }
2137
2138 // Next find the last paragraph containing part of the range
2139 node = m_children.GetFirst();
2140 while (node)
2141 {
2142 // child is a paragraph
2143 wxRichTextObject* child = node->GetData();
2144 const wxRichTextRange& r = child->GetRange();
2145
2146 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2147 {
2148 endPara = node;
2149 break;
2150 }
2151
2152 node = node->GetNext();
2153 }
2154
2155 if (!startPara || !endPara)
2156 return false;
2157
2158 // Now we can add up the sizes
2159 for (node = startPara; node ; node = node->GetNext())
2160 {
2161 // child is a paragraph
2162 wxRichTextObject* child = node->GetData();
2163 const wxRichTextRange& childRange = child->GetRange();
2164 wxRichTextRange rangeToFind = range;
2165 rangeToFind.LimitTo(childRange);
2166
603f702b
JS
2167 if (child->IsTopLevel())
2168 rangeToFind = child->GetOwnRange();
2169
5d7836c4
JS
2170 wxSize childSize;
2171
2172 int childDescent = 0;
8db2e3ef 2173 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
5d7836c4
JS
2174
2175 descent = wxMax(childDescent, descent);
2176
2177 sz.x = wxMax(sz.x, childSize.x);
2178 sz.y += childSize.y;
2179
2180 if (node == endPara)
2181 break;
2182 }
2183
2184 size = sz;
2185
2186 return true;
2187}
2188
2189/// Get the paragraph at the given position
2190wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2191{
2192 if (caretPosition)
2193 pos ++;
2194
2195 // First find the first paragraph whose starting position is within the range.
2196 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2197 while (node)
2198 {
2199 // child is a paragraph
2200 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2201 // wxASSERT (child != NULL);
5d7836c4 2202
603f702b
JS
2203 if (child)
2204 {
2205 // Return first child in buffer if position is -1
2206 // if (pos == -1)
2207 // return child;
5d7836c4 2208
603f702b
JS
2209 if (child->GetRange().Contains(pos))
2210 return child;
2211 }
5d7836c4
JS
2212
2213 node = node->GetNext();
2214 }
2215 return NULL;
2216}
2217
2218/// Get the line at the given position
2219wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2220{
2221 if (caretPosition)
2222 pos ++;
2223
2224 // First find the first paragraph whose starting position is within the range.
2225 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2226 while (node)
2227 {
7051fa41
JS
2228 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2229 if (obj->GetRange().Contains(pos))
5d7836c4 2230 {
7051fa41
JS
2231 // child is a paragraph
2232 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2233 // wxASSERT (child != NULL);
7051fa41 2234
603f702b 2235 if (child)
7051fa41 2236 {
603f702b
JS
2237 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2238 while (node2)
2239 {
2240 wxRichTextLine* line = node2->GetData();
5d7836c4 2241
603f702b 2242 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2243
603f702b 2244 if (range.Contains(pos) ||
5d7836c4 2245
603f702b
JS
2246 // If the position is end-of-paragraph, then return the last line of
2247 // of the paragraph.
2248 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2249 return line;
5d7836c4 2250
603f702b
JS
2251 node2 = node2->GetNext();
2252 }
7051fa41 2253 }
7fe8059f 2254 }
5d7836c4
JS
2255
2256 node = node->GetNext();
2257 }
2258
2259 int lineCount = GetLineCount();
2260 if (lineCount > 0)
2261 return GetLineForVisibleLineNumber(lineCount-1);
2262 else
2263 return NULL;
2264}
2265
2266/// Get the line at the given y pixel position, or the last line.
2267wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2268{
2269 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2270 while (node)
2271 {
2272 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2273 // wxASSERT (child != NULL);
5d7836c4 2274
603f702b 2275 if (child)
5d7836c4 2276 {
603f702b
JS
2277 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2278 while (node2)
2279 {
2280 wxRichTextLine* line = node2->GetData();
5d7836c4 2281
603f702b 2282 wxRect rect(line->GetRect());
5d7836c4 2283
603f702b
JS
2284 if (y <= rect.GetBottom())
2285 return line;
5d7836c4 2286
603f702b
JS
2287 node2 = node2->GetNext();
2288 }
7fe8059f 2289 }
5d7836c4
JS
2290
2291 node = node->GetNext();
2292 }
2293
2294 // Return last line
2295 int lineCount = GetLineCount();
2296 if (lineCount > 0)
2297 return GetLineForVisibleLineNumber(lineCount-1);
2298 else
2299 return NULL;
2300}
2301
2302/// Get the number of visible lines
2303int wxRichTextParagraphLayoutBox::GetLineCount() const
2304{
2305 int count = 0;
2306
2307 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2308 while (node)
2309 {
2310 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2311 // wxASSERT (child != NULL);
2312
2313 if (child)
2314 count += child->GetLines().GetCount();
5d7836c4 2315
5d7836c4
JS
2316 node = node->GetNext();
2317 }
2318 return count;
2319}
2320
2321
2322/// Get the paragraph for a given line
2323wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2324{
1e967276 2325 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2326}
2327
2328/// Get the line size at the given position
2329wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2330{
2331 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2332 if (line)
2333 {
2334 return line->GetSize();
2335 }
2336 else
2337 return wxSize(0, 0);
2338}
2339
2340
2341/// Convenience function to add a paragraph of text
24777478 2342wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2343{
fe5aa22c 2344 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2345 // be combined at display time.
2346 // Divide into paragraph and character styles.
3e541562 2347
24777478
JS
2348 wxRichTextAttr defaultCharStyle;
2349 wxRichTextAttr defaultParaStyle;
4f32b3cf 2350
5607c890
JS
2351 // If the default style is a named paragraph style, don't apply any character formatting
2352 // to the initial text string.
2353 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2354 {
2355 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2356 if (def)
2357 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2358 }
2359 else
2360 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2361
24777478
JS
2362 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2363 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2364
2365 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
32423dd8 2366 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4
JS
2367
2368 AppendChild(para);
2369
2370 UpdateRanges();
5d7836c4
JS
2371
2372 return para->GetRange();
2373}
2374
2375/// Adds multiple paragraphs, based on newlines.
24777478 2376wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2377{
fe5aa22c 2378 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2379 // be combined at display time.
2380 // Divide into paragraph and character styles.
3e541562 2381
24777478
JS
2382 wxRichTextAttr defaultCharStyle;
2383 wxRichTextAttr defaultParaStyle;
5607c890
JS
2384
2385 // If the default style is a named paragraph style, don't apply any character formatting
2386 // to the initial text string.
2387 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2388 {
2389 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2390 if (def)
2391 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2392 }
2393 else
2394 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2395
24777478
JS
2396 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2397 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2398
5d7836c4
JS
2399 wxRichTextParagraph* firstPara = NULL;
2400 wxRichTextParagraph* lastPara = NULL;
2401
2402 wxRichTextRange range(-1, -1);
0ca07313 2403
5d7836c4 2404 size_t i = 0;
28f92d74 2405 size_t len = text.length();
5d7836c4 2406 wxString line;
4f32b3cf 2407 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2408 para->GetAttributes().GetTextBoxAttr().Reset();
0ca07313
JS
2409
2410 AppendChild(para);
2411
2412 firstPara = para;
2413 lastPara = para;
2414
5d7836c4
JS
2415 while (i < len)
2416 {
2417 wxChar ch = text[i];
2418 if (ch == wxT('\n') || ch == wxT('\r'))
2419 {
99404ab0
JS
2420 if (i != (len-1))
2421 {
2422 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2423 plainText->SetText(line);
0ca07313 2424
99404ab0 2425 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2426 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4 2427
99404ab0 2428 AppendChild(para);
0ca07313 2429
99404ab0
JS
2430 lastPara = para;
2431 line = wxEmptyString;
2432 }
5d7836c4
JS
2433 }
2434 else
2435 line += ch;
2436
2437 i ++;
2438 }
0ca07313 2439
7fe8059f 2440 if (!line.empty())
5d7836c4 2441 {
0ca07313
JS
2442 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2443 plainText->SetText(line);
5d7836c4
JS
2444 }
2445
5d7836c4 2446 UpdateRanges();
0ca07313 2447
0ca07313 2448 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2449}
2450
2451/// Convenience function to add an image
24777478 2452wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2453{
fe5aa22c 2454 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2455 // be combined at display time.
2456 // Divide into paragraph and character styles.
3e541562 2457
24777478
JS
2458 wxRichTextAttr defaultCharStyle;
2459 wxRichTextAttr defaultParaStyle;
5607c890
JS
2460
2461 // If the default style is a named paragraph style, don't apply any character formatting
2462 // to the initial text string.
2463 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2464 {
2465 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2466 if (def)
2467 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2468 }
2469 else
2470 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2471
24777478
JS
2472 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2473 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2474
4f32b3cf 2475 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
32423dd8 2476 para->GetAttributes().GetTextBoxAttr().Reset();
4f32b3cf
JS
2477 AppendChild(para);
2478 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2479
5d7836c4 2480 UpdateRanges();
5d7836c4
JS
2481
2482 return para->GetRange();
2483}
2484
2485
2486/// Insert fragment into this box at the given position. If partialParagraph is true,
2487/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2488/// marker.
5d7836c4 2489
0ca07313 2490bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2491{
5d7836c4
JS
2492 // First, find the first paragraph whose starting position is within the range.
2493 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2494 if (para)
2495 {
24777478 2496 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2497
5d7836c4
JS
2498 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2499
2500 // Now split at this position, returning the object to insert the new
2501 // ones in front of.
2502 wxRichTextObject* nextObject = para->SplitAt(position);
2503
2504 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2505 // text, for example, so let's optimize.
2506
2507 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2508 {
2509 // Add the first para to this para...
2510 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2511 if (!firstParaNode)
2512 return false;
2513
2514 // Iterate through the fragment paragraph inserting the content into this paragraph.
2515 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2516 wxASSERT (firstPara != NULL);
2517
2518 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2519 while (objectNode)
2520 {
2521 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2522
5d7836c4
JS
2523 if (!nextObject)
2524 {
2525 // Append
2526 para->AppendChild(newObj);
2527 }
2528 else
2529 {
2530 // Insert before nextObject
2531 para->InsertChild(newObj, nextObject);
2532 }
7fe8059f 2533
5d7836c4
JS
2534 objectNode = objectNode->GetNext();
2535 }
2536
2537 return true;
2538 }
2539 else
2540 {
2541 // Procedure for inserting a fragment consisting of a number of
2542 // paragraphs:
2543 //
2544 // 1. Remove and save the content that's after the insertion point, for adding
2545 // back once we've added the fragment.
2546 // 2. Add the content from the first fragment paragraph to the current
2547 // paragraph.
2548 // 3. Add remaining fragment paragraphs after the current paragraph.
2549 // 4. Add back the saved content from the first paragraph. If partialParagraph
2550 // is true, add it to the last paragraph added and not a new one.
2551
2552 // 1. Remove and save objects after split point.
2553 wxList savedObjects;
2554 if (nextObject)
2555 para->MoveToList(nextObject, savedObjects);
2556
2557 // 2. Add the content from the 1st fragment paragraph.
2558 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2559 if (!firstParaNode)
2560 return false;
2561
2562 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2563 wxASSERT(firstPara != NULL);
2564
6c0ea513
JS
2565 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2566 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2567
2568 // Save empty paragraph attributes for appending later
2569 // These are character attributes deliberately set for a new paragraph. Without this,
2570 // we couldn't pass default attributes when appending a new paragraph.
24777478 2571 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2572
5d7836c4 2573 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2574
2575 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2576 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2577
5d7836c4
JS
2578 while (objectNode)
2579 {
c025e094 2580 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2581
c025e094
JS
2582 // Append
2583 para->AppendChild(newObj);
7fe8059f 2584
5d7836c4
JS
2585 objectNode = objectNode->GetNext();
2586 }
2587
2588 // 3. Add remaining fragment paragraphs after the current paragraph.
2589 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2590 wxRichTextObject* nextParagraph = NULL;
2591 if (nextParagraphNode)
2592 nextParagraph = nextParagraphNode->GetData();
2593
2594 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2595 wxRichTextParagraph* finalPara = para;
2596
99404ab0
JS
2597 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2598
5d7836c4 2599 // If there was only one paragraph, we need to insert a new one.
99404ab0 2600 while (i)
5d7836c4 2601 {
99404ab0
JS
2602 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2603 wxASSERT( para != NULL );
5d7836c4 2604
99404ab0 2605 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2606
2607 if (nextParagraph)
2608 InsertChild(finalPara, nextParagraph);
2609 else
7fe8059f 2610 AppendChild(finalPara);
99404ab0
JS
2611
2612 i = i->GetNext();
5d7836c4 2613 }
5d7836c4 2614
99404ab0
JS
2615 // If there was only one paragraph, or we have full paragraphs in our fragment,
2616 // we need to insert a new one.
2617 if (needExtraPara)
2618 {
2619 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2620
2621 if (nextParagraph)
2622 InsertChild(finalPara, nextParagraph);
2623 else
2624 AppendChild(finalPara);
5d7836c4
JS
2625 }
2626
2627 // 4. Add back the remaining content.
2628 if (finalPara)
2629 {
c025e094
JS
2630 if (nextObject)
2631 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2632
2633 // Ensure there's at least one object
2634 if (finalPara->GetChildCount() == 0)
2635 {
7fe8059f 2636 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2637 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2638
2639 finalPara->AppendChild(text);
2640 }
2641 }
2642
6c0ea513
JS
2643 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2644 finalPara->SetAttributes(firstPara->GetAttributes());
2645 else if (finalPara && finalPara != para)
99404ab0
JS
2646 finalPara->SetAttributes(originalAttr);
2647
5d7836c4
JS
2648 return true;
2649 }
2650 }
2651 else
2652 {
2653 // Append
2654 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2655 while (i)
2656 {
2657 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2658 wxASSERT( para != NULL );
7fe8059f 2659
5d7836c4 2660 AppendChild(para->Clone());
7fe8059f 2661
5d7836c4
JS
2662 i = i->GetNext();
2663 }
2664
2665 return true;
2666 }
5d7836c4
JS
2667}
2668
2669/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2670/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2671bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2672{
2673 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2674 while (i)
2675 {
2676 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2677 wxASSERT( para != NULL );
2678
2679 if (!para->GetRange().IsOutside(range))
2680 {
2681 fragment.AppendChild(para->Clone());
7fe8059f 2682 }
5d7836c4
JS
2683 i = i->GetNext();
2684 }
2685
2686 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2687 if (!fragment.IsEmpty())
2688 {
5d7836c4
JS
2689 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2690 wxASSERT( firstPara != NULL );
2691
0e190fa2
JS
2692 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2693 wxASSERT( lastPara != NULL );
2694
2695 if (!firstPara || !lastPara)
2696 return false;
2697
2698 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2699
2700 long firstPos = firstPara->GetRange().GetStart();
2701
2702 // Adjust for renumbering from zero
2703 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2704
2705 long end;
2706 fragment.CalculateRange(0, end);
2707
5d7836c4 2708 // Chop off the start of the paragraph
0e190fa2 2709 if (topTailRange.GetStart() > 0)
5d7836c4 2710 {
0e190fa2 2711 wxRichTextRange r(0, topTailRange.GetStart()-1);
5d7836c4
JS
2712 firstPara->DeleteRange(r);
2713
2714 // Make sure the numbering is correct
0e190fa2 2715 fragment.CalculateRange(0, end);
5d7836c4
JS
2716
2717 // Now, we've deleted some positions, so adjust the range
2718 // accordingly.
0e190fa2
JS
2719 topTailRange.SetStart(range.GetLength());
2720 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2721 }
2722 else
2723 {
2724 topTailRange.SetStart(range.GetLength());
2725 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
5d7836c4
JS
2726 }
2727
61e6149e 2728 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
5d7836c4 2729 {
0e190fa2 2730 lastPara->DeleteRange(topTailRange);
5d7836c4
JS
2731
2732 // Make sure the numbering is correct
2733 long end;
0e190fa2 2734 fragment.CalculateRange(0, end);
5d7836c4
JS
2735
2736 // We only have part of a paragraph at the end
2737 fragment.SetPartialParagraph(true);
2738 }
2739 else
2740 {
0e190fa2
JS
2741 // We have a partial paragraph (don't save last new paragraph marker)
2742 // or complete paragraph
2743 fragment.SetPartialParagraph(isFragment);
5d7836c4
JS
2744 }
2745 }
2746
2747 return true;
2748}
2749
2750/// Given a position, get the number of the visible line (potentially many to a paragraph),
2751/// starting from zero at the start of the buffer.
2752long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2753{
2754 if (caretPosition)
2755 pos ++;
2756
2757 int lineCount = 0;
2758
2759 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2760 while (node)
2761 {
2762 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2763 // wxASSERT( child != NULL );
5d7836c4 2764
603f702b 2765 if (child)
5d7836c4 2766 {
603f702b 2767 if (child->GetRange().Contains(pos))
5d7836c4 2768 {
603f702b
JS
2769 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2770 while (node2)
5d7836c4 2771 {
603f702b
JS
2772 wxRichTextLine* line = node2->GetData();
2773 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2774
603f702b
JS
2775 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2776 {
2777 // If the caret is displayed at the end of the previous wrapped line,
2778 // we want to return the line it's _displayed_ at (not the actual line
2779 // containing the position).
2780 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2781 return lineCount - 1;
2782 else
2783 return lineCount;
2784 }
7fe8059f 2785
603f702b
JS
2786 lineCount ++;
2787
2788 node2 = node2->GetNext();
2789 }
2790 // If we didn't find it in the lines, it must be
2791 // the last position of the paragraph. So return the last line.
2792 return lineCount-1;
5d7836c4 2793 }
603f702b
JS
2794 else
2795 lineCount += child->GetLines().GetCount();
5d7836c4 2796 }
5d7836c4
JS
2797
2798 node = node->GetNext();
2799 }
2800
2801 // Not found
2802 return -1;
2803}
2804
2805/// Given a line number, get the corresponding wxRichTextLine object.
2806wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2807{
2808 int lineCount = 0;
2809
2810 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2811 while (node)
2812 {
2813 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2814 // wxASSERT(child != NULL);
5d7836c4 2815
603f702b 2816 if (child)
5d7836c4 2817 {
603f702b 2818 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2819 {
603f702b
JS
2820 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2821 while (node2)
2822 {
2823 wxRichTextLine* line = node2->GetData();
7fe8059f 2824
603f702b
JS
2825 if (lineCount == lineNumber)
2826 return line;
5d7836c4 2827
603f702b 2828 lineCount ++;
7fe8059f 2829
603f702b
JS
2830 node2 = node2->GetNext();
2831 }
7fe8059f 2832 }
603f702b
JS
2833 else
2834 lineCount += child->GetLines().GetCount();
5d7836c4 2835 }
5d7836c4
JS
2836
2837 node = node->GetNext();
2838 }
2839
2840 // Didn't find it
2841 return NULL;
2842}
2843
2844/// Delete range from layout.
2845bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2846{
2847 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2848
99404ab0 2849 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2850 while (node)
2851 {
2852 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2853 // wxASSERT (obj != NULL);
5d7836c4
JS
2854
2855 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2856
603f702b 2857 if (obj)
5d7836c4 2858 {
603f702b 2859 // Delete the range in each paragraph
99404ab0 2860
603f702b 2861 if (!obj->GetRange().IsOutside(range))
5d7836c4 2862 {
603f702b
JS
2863 // Deletes the content of this object within the given range
2864 obj->DeleteRange(range);
99404ab0 2865
603f702b
JS
2866 wxRichTextRange thisRange = obj->GetRange();
2867 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2868
603f702b
JS
2869 // If the whole paragraph is within the range to delete,
2870 // delete the whole thing.
2871 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2872 {
603f702b
JS
2873 // Delete the whole object
2874 RemoveChild(obj, true);
2875 obj = NULL;
99404ab0 2876 }
603f702b
JS
2877 else if (!firstPara)
2878 firstPara = obj;
5d7836c4 2879
603f702b
JS
2880 // If the range includes the paragraph end, we need to join this
2881 // and the next paragraph.
2882 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2883 {
603f702b
JS
2884 // We need to move the objects from the next paragraph
2885 // to this paragraph
2886
2887 wxRichTextParagraph* nextParagraph = NULL;
2888 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2889 nextParagraph = obj;
6c0ea513 2890 else
603f702b
JS
2891 {
2892 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2893 if (next)
2894 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2895 }
5d7836c4 2896
603f702b
JS
2897 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2898
2899 wxRichTextAttr nextParaAttr;
2900 if (applyFinalParagraphStyle)
2901 {
2902 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2903 // not the next one.
2904 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2905 nextParaAttr = thisAttr;
2906 else
2907 nextParaAttr = nextParagraph->GetAttributes();
2908 }
5d7836c4 2909
603f702b 2910 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2911 {
603f702b
JS
2912 // Move the objects to the previous para
2913 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2914
603f702b
JS
2915 while (node1)
2916 {
2917 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2918
603f702b 2919 firstPara->AppendChild(obj1);
5d7836c4 2920
603f702b
JS
2921 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2922 nextParagraph->GetChildren().Erase(node1);
99404ab0 2923
603f702b
JS
2924 node1 = next1;
2925 }
5d7836c4 2926
603f702b
JS
2927 // Delete the paragraph
2928 RemoveChild(nextParagraph, true);
2929 }
fa01bfdd 2930
603f702b
JS
2931 // Avoid empty paragraphs
2932 if (firstPara && firstPara->GetChildren().GetCount() == 0)
2933 {
2934 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2935 firstPara->AppendChild(text);
2936 }
99404ab0 2937
603f702b
JS
2938 if (applyFinalParagraphStyle)
2939 firstPara->SetAttributes(nextParaAttr);
2940
2941 return true;
2942 }
5d7836c4
JS
2943 }
2944 }
7fe8059f 2945
5d7836c4
JS
2946 node = next;
2947 }
7fe8059f 2948
5d7836c4
JS
2949 return true;
2950}
2951
2952/// Get any text in this object for the given range
2953wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
2954{
2955 int lineCount = 0;
2956 wxString text;
2957 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2958 while (node)
2959 {
2960 wxRichTextObject* child = node->GetData();
2961 if (!child->GetRange().IsOutside(range))
2962 {
5d7836c4
JS
2963 wxRichTextRange childRange = range;
2964 childRange.LimitTo(child->GetRange());
7fe8059f 2965
5d7836c4 2966 wxString childText = child->GetTextForRange(childRange);
7fe8059f 2967
5d7836c4
JS
2968 text += childText;
2969
1a75935d 2970 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
2971 text += wxT("\n");
2972
5d7836c4
JS
2973 lineCount ++;
2974 }
2975 node = node->GetNext();
2976 }
2977
2978 return text;
2979}
2980
2981/// Get all the text
2982wxString wxRichTextParagraphLayoutBox::GetText() const
2983{
c99f1b0f 2984 return GetTextForRange(GetOwnRange());
5d7836c4
JS
2985}
2986
2987/// Get the paragraph by number
2988wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
2989{
27e20452 2990 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
2991 return NULL;
2992
2993 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
2994}
2995
2996/// Get the length of the paragraph
2997int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
2998{
2999 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3000 if (para)
3001 return para->GetRange().GetLength() - 1; // don't include newline
3002 else
3003 return 0;
3004}
3005
3006/// Get the text of the paragraph
3007wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3008{
3009 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3010 if (para)
3011 return para->GetTextForRange(para->GetRange());
3012 else
3013 return wxEmptyString;
3014}
3015
3016/// Convert zero-based line column and paragraph number to a position.
3017long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3018{
3019 wxRichTextParagraph* para = GetParagraphAtLine(y);
3020 if (para)
3021 {
3022 return para->GetRange().GetStart() + x;
3023 }
3024 else
3025 return -1;
3026}
3027
3028/// Convert zero-based position to line column and paragraph number
3029bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3030{
3031 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3032 if (para)
3033 {
3034 int count = 0;
3035 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3036 while (node)
3037 {
3038 wxRichTextObject* child = node->GetData();
3039 if (child == para)
3040 break;
3041 count ++;
3042 node = node->GetNext();
3043 }
3044
3045 *y = count;
3046 *x = pos - para->GetRange().GetStart();
3047
3048 return true;
3049 }
3050 else
3051 return false;
3052}
3053
3054/// Get the leaf object in a paragraph at this position.
3055/// Given a line number, get the corresponding wxRichTextLine object.
3056wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3057{
3058 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3059 if (para)
3060 {
3061 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 3062
5d7836c4
JS
3063 while (node)
3064 {
3065 wxRichTextObject* child = node->GetData();
3066 if (child->GetRange().Contains(position))
3067 return child;
7fe8059f 3068
5d7836c4
JS
3069 node = node->GetNext();
3070 }
3071 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3072 return para->GetChildren().GetLast()->GetData();
3073 }
3074 return NULL;
3075}
3076
3077/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 3078bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
3079{
3080 bool characterStyle = false;
3081 bool paragraphStyle = false;
3082
3083 if (style.IsCharacterStyle())
3084 characterStyle = true;
3085 if (style.IsParagraphStyle())
3086 paragraphStyle = true;
3087
603f702b
JS
3088 wxRichTextBuffer* buffer = GetBuffer();
3089
59509217
JS
3090 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3091 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3092 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3093 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 3094 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 3095 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
3096
3097 // Apply paragraph style first, if any
24777478 3098 wxRichTextAttr wholeStyle(style);
523d2f14 3099
603f702b 3100 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 3101 {
603f702b 3102 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 3103 if (def)
603f702b 3104 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 3105 }
59509217
JS
3106
3107 // Limit the attributes to be set to the content to only character attributes.
24777478 3108 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
3109 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3110
603f702b 3111 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 3112 {
603f702b 3113 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 3114 if (def)
603f702b 3115 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
3116 }
3117
5d7836c4
JS
3118 // If we are associated with a control, make undoable; otherwise, apply immediately
3119 // to the data.
3120
603f702b 3121 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3122
3123 wxRichTextAction* action = NULL;
7fe8059f 3124
5d7836c4
JS
3125 if (haveControl && withUndo)
3126 {
603f702b 3127 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3128 action->SetRange(range);
603f702b 3129 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3130 }
3131
3132 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3133 while (node)
3134 {
3135 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3136 // wxASSERT (para != NULL);
5d7836c4
JS
3137
3138 if (para && para->GetChildCount() > 0)
3139 {
3140 // Stop searching if we're beyond the range of interest
3141 if (para->GetRange().GetStart() > range.GetEnd())
3142 break;
3143
3144 if (!para->GetRange().IsOutside(range))
3145 {
3146 // We'll be using a copy of the paragraph to make style changes,
3147 // not updating the buffer directly.
4e09ebe8 3148 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3149
5d7836c4
JS
3150 if (haveControl && withUndo)
3151 {
3152 newPara = new wxRichTextParagraph(*para);
3153 action->GetNewParagraphs().AppendChild(newPara);
3154
3155 // Also store the old ones for Undo
3156 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3157 }
3158 else
3159 newPara = para;
41a85215 3160
a7ed48a5
JS
3161 // If we're specifying paragraphs only, then we really mean character formatting
3162 // to be included in the paragraph style
3163 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3164 {
aeb6ebe2
JS
3165 if (removeStyle)
3166 {
3167 // Removes the given style from the paragraph
3168 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3169 }
3170 else if (resetExistingStyle)
523d2f14
JS
3171 newPara->GetAttributes() = wholeStyle;
3172 else
59509217 3173 {
523d2f14
JS
3174 if (applyMinimal)
3175 {
3176 // Only apply attributes that will make a difference to the combined
3177 // style as seen on the display
603f702b 3178 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3179 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3180 }
3181 else
3182 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3183 }
59509217 3184 }
5d7836c4 3185
5912d19e 3186 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3187 // since they will computed as needed. Only apply the character styling if it's _only_
3188 // character styling. This policy is subject to change and might be put under user control.
3189
59509217
JS
3190 // Hm. we might well be applying a mix of paragraph and character styles, in which
3191 // case we _do_ want to apply character styles regardless of what para styles are set.
3192 // But if we're applying a paragraph style, which has some character attributes, but
3193 // we only want the paragraphs to hold this character style, then we _don't_ want to
3194 // apply the character style. So we need to be able to choose.
3195
f1d800d9 3196 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3197 {
3198 wxRichTextRange childRange(range);
3199 childRange.LimitTo(newPara->GetRange());
7fe8059f 3200
5d7836c4
JS
3201 // Find the starting position and if necessary split it so
3202 // we can start applying a different style.
3203 // TODO: check that the style actually changes or is different
3204 // from style outside of range
4e09ebe8
JS
3205 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3206 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3207
5d7836c4
JS
3208 if (childRange.GetStart() == newPara->GetRange().GetStart())
3209 firstObject = newPara->GetChildren().GetFirst()->GetData();
3210 else
3211 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3212
5d7836c4
JS
3213 // Increment by 1 because we're apply the style one _after_ the split point
3214 long splitPoint = childRange.GetEnd();
3215 if (splitPoint != newPara->GetRange().GetEnd())
3216 splitPoint ++;
7fe8059f 3217
5d7836c4 3218 // Find last object
4b3483e7 3219 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3220 lastObject = newPara->GetChildren().GetLast()->GetData();
3221 else
3222 // lastObject is set as a side-effect of splitting. It's
3223 // returned as the object before the new object.
3224 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3225
5d7836c4
JS
3226 wxASSERT(firstObject != NULL);
3227 wxASSERT(lastObject != NULL);
7fe8059f 3228
5d7836c4
JS
3229 if (!firstObject || !lastObject)
3230 continue;
7fe8059f 3231
5d7836c4
JS
3232 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3233 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3234
4c9847e1
MW
3235 wxASSERT(firstNode);
3236 wxASSERT(lastNode);
7fe8059f 3237
5d7836c4 3238 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3239
5d7836c4
JS
3240 while (node2)
3241 {
3242 wxRichTextObject* child = node2->GetData();
7fe8059f 3243
aeb6ebe2
JS
3244 if (removeStyle)
3245 {
3246 // Removes the given style from the paragraph
3247 wxRichTextRemoveStyle(child->GetAttributes(), style);
3248 }
3249 else if (resetExistingStyle)
523d2f14
JS
3250 child->GetAttributes() = characterAttributes;
3251 else
59509217 3252 {
523d2f14
JS
3253 if (applyMinimal)
3254 {
3255 // Only apply attributes that will make a difference to the combined
3256 // style as seen on the display
603f702b 3257 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3258 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3259 }
3260 else
3261 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3262 }
59509217 3263
5d7836c4
JS
3264 if (node2 == lastNode)
3265 break;
7fe8059f 3266
5d7836c4
JS
3267 node2 = node2->GetNext();
3268 }
3269 }
3270 }
3271 }
3272
3273 node = node->GetNext();
3274 }
3275
3276 // Do action, or delay it until end of batch.
3277 if (haveControl && withUndo)
603f702b 3278 buffer->SubmitAction(action);
5d7836c4
JS
3279
3280 return true;
3281}
3282
603f702b
JS
3283// Just change the attributes for this single object.
3284void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3285{
603f702b 3286 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3287 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3288 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3289 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3290
cdaed652 3291 wxRichTextAction *action = NULL;
603f702b
JS
3292 wxRichTextAttr newAttr = obj->GetAttributes();
3293 if (resetExistingStyle)
3294 newAttr = textAttr;
3295 else
3296 newAttr.Apply(textAttr);
cdaed652
VZ
3297
3298 if (haveControl && withUndo)
3299 {
603f702b
JS
3300 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3301 action->SetRange(obj->GetRange().FromInternal());
3302 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3303 action->MakeObject(obj);
bec80f4f 3304
603f702b 3305 action->GetAttributes() = newAttr;
cdaed652
VZ
3306 }
3307 else
603f702b 3308 obj->GetAttributes() = newAttr;
cdaed652
VZ
3309
3310 if (haveControl && withUndo)
603f702b 3311 buffer->SubmitAction(action);
cdaed652
VZ
3312}
3313
5d7836c4 3314/// Get the text attributes for this position.
24777478 3315bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3316{
fe5aa22c
JS
3317 return DoGetStyle(position, style, true);
3318}
e191ee87 3319
24777478 3320bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3321{
3322 return DoGetStyle(position, style, false);
3323}
3324
fe5aa22c
JS
3325/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3326/// context attributes.
24777478 3327bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3328{
4e09ebe8 3329 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3330
5d7836c4 3331 if (style.IsParagraphStyle())
fe5aa22c 3332 {
5d7836c4 3333 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3334 if (obj)
3335 {
fe5aa22c
JS
3336 if (combineStyles)
3337 {
3338 // Start with the base style
3339 style = GetAttributes();
32423dd8 3340 style.GetTextBoxAttr().Reset();
e191ee87 3341
fe5aa22c
JS
3342 // Apply the paragraph style
3343 wxRichTextApplyStyle(style, obj->GetAttributes());
3344 }
3345 else
3346 style = obj->GetAttributes();
5912d19e 3347
fe5aa22c
JS
3348 return true;
3349 }
5d7836c4
JS
3350 }
3351 else
fe5aa22c
JS
3352 {
3353 obj = GetLeafObjectAtPosition(position);
3354 if (obj)
3355 {
fe5aa22c
JS
3356 if (combineStyles)
3357 {
3358 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3359 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3360 }
3361 else
3362 style = obj->GetAttributes();
5912d19e 3363
fe5aa22c
JS
3364 return true;
3365 }
fe5aa22c
JS
3366 }
3367 return false;
5d7836c4
JS
3368}
3369
59509217
JS
3370static bool wxHasStyle(long flags, long style)
3371{
3372 return (flags & style) != 0;
3373}
3374
3375/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3376/// content.
24777478
JS
3377bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3378{
3379 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3380
3381 return true;
3382}
3383
3384/// Get the combined style for a range - if any attribute is different within the range,
3385/// that attribute is not present within the flags.
3386/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3387/// nested.
3388bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3389{
24777478
JS
3390 style = wxRichTextAttr();
3391
3392 wxRichTextAttr clashingAttr;
3393 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3394
24777478
JS
3395 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3396 while (node)
59509217 3397 {
603f702b
JS
3398 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3399 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3400 {
24777478 3401 if (para->GetChildren().GetCount() == 0)
59509217 3402 {
603f702b 3403 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3404
24777478 3405 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
59509217
JS
3406 }
3407 else
3408 {
24777478
JS
3409 wxRichTextRange paraRange(para->GetRange());
3410 paraRange.LimitTo(range);
59509217 3411
24777478
JS
3412 // First collect paragraph attributes only
3413 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3414 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3415 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
9c4cb611 3416
24777478
JS
3417 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3418
3419 while (childNode)
59509217 3420 {
24777478
JS
3421 wxRichTextObject* child = childNode->GetData();
3422 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3423 {
603f702b 3424 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3425
24777478
JS
3426 // Now collect character attributes only
3427 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3428
24777478
JS
3429 CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
3430 }
59509217 3431
24777478 3432 childNode = childNode->GetNext();
59509217
JS
3433 }
3434 }
59509217 3435 }
24777478 3436 node = node->GetNext();
59509217 3437 }
24777478
JS
3438 return true;
3439}
59509217 3440
24777478
JS
3441/// Set default style
3442bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3443{
3444 m_defaultAttributes = style;
3445 return true;
3446}
59509217 3447
24777478
JS
3448/// Test if this whole range has character attributes of the specified kind. If any
3449/// of the attributes are different within the range, the test fails. You
3450/// can use this to implement, for example, bold button updating. style must have
3451/// flags indicating which attributes are of interest.
3452bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3453{
3454 int foundCount = 0;
3455 int matchingCount = 0;
59509217 3456
24777478
JS
3457 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3458 while (node)
59509217 3459 {
24777478 3460 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3461 // wxASSERT (para != NULL);
59509217 3462
24777478 3463 if (para)
59509217 3464 {
24777478
JS
3465 // Stop searching if we're beyond the range of interest
3466 if (para->GetRange().GetStart() > range.GetEnd())
3467 return foundCount == matchingCount && foundCount != 0;
59509217 3468
24777478 3469 if (!para->GetRange().IsOutside(range))
59509217 3470 {
24777478 3471 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3472
24777478
JS
3473 while (node2)
3474 {
3475 wxRichTextObject* child = node2->GetData();
3476 // Allow for empty string if no buffer
3477 wxRichTextRange childRange = child->GetRange();
3478 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3479 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3480
345c78ca 3481 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
24777478
JS
3482 {
3483 foundCount ++;
3484 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3485
32423dd8 3486 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
24777478
JS
3487 matchingCount ++;
3488 }
59509217 3489
24777478
JS
3490 node2 = node2->GetNext();
3491 }
59509217
JS
3492 }
3493 }
59509217 3494
24777478 3495 node = node->GetNext();
59509217
JS
3496 }
3497
24777478
JS
3498 return foundCount == matchingCount && foundCount != 0;
3499}
59509217 3500
24777478
JS
3501/// Test if this whole range has paragraph attributes of the specified kind. If any
3502/// of the attributes are different within the range, the test fails. You
3503/// can use this to implement, for example, centering button updating. style must have
3504/// flags indicating which attributes are of interest.
3505bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3506{
3507 int foundCount = 0;
3508 int matchingCount = 0;
3509
3510 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3511 while (node)
38f833b1 3512 {
24777478 3513 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3514 // wxASSERT (para != NULL);
24777478
JS
3515
3516 if (para)
38f833b1 3517 {
24777478
JS
3518 // Stop searching if we're beyond the range of interest
3519 if (para->GetRange().GetStart() > range.GetEnd())
3520 return foundCount == matchingCount && foundCount != 0;
3521
3522 if (!para->GetRange().IsOutside(range))
38f833b1 3523 {
24777478
JS
3524 wxRichTextAttr textAttr = GetAttributes();
3525 // Apply the paragraph style
3526 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3527
3528 foundCount ++;
32423dd8 3529 if (textAttr.EqPartial(style, false /* strong test */))
24777478 3530 matchingCount ++;
38f833b1
JS
3531 }
3532 }
24777478
JS
3533
3534 node = node->GetNext();
38f833b1 3535 }
24777478
JS
3536 return foundCount == matchingCount && foundCount != 0;
3537}
5d7836c4 3538
cc2aecde
JS
3539void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3540{
3541 wxRichTextBuffer* buffer = GetBuffer();
3542 if (buffer && buffer->GetRichTextCtrl())
3543 buffer->GetRichTextCtrl()->PrepareContent(container);
3544}
3545
590a0f8b
JS
3546/// Set character or paragraph properties
3547bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3548{
3549 wxRichTextBuffer* buffer = GetBuffer();
3550
3551 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3552 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3553 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3554 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3555 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3556
3557 // If we are associated with a control, make undoable; otherwise, apply immediately
3558 // to the data.
3559
3560 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3561
3562 wxRichTextAction* action = NULL;
3563
3564 if (haveControl && withUndo)
3565 {
3566 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3567 action->SetRange(range);
3568 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3569 }
3570
3571 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3572 while (node)
3573 {
3574 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3575 // wxASSERT (para != NULL);
3576
3577 if (para && para->GetChildCount() > 0)
3578 {
3579 // Stop searching if we're beyond the range of interest
3580 if (para->GetRange().GetStart() > range.GetEnd())
3581 break;
3582
3583 if (!para->GetRange().IsOutside(range))
3584 {
3585 // We'll be using a copy of the paragraph to make style changes,
3586 // not updating the buffer directly.
3587 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3588
3589 if (haveControl && withUndo)
3590 {
3591 newPara = new wxRichTextParagraph(*para);
3592 action->GetNewParagraphs().AppendChild(newPara);
3593
3594 // Also store the old ones for Undo
3595 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3596 }
3597 else
3598 newPara = para;
3599
3600 if (parasOnly)
3601 {
3602 if (removeProperties)
3603 {
3604 // Removes the given style from the paragraph
3605 // TODO
3606 newPara->GetProperties().RemoveProperties(properties);
3607 }
3608 else if (resetExistingProperties)
3609 newPara->GetProperties() = properties;
3610 else
3611 newPara->GetProperties().MergeProperties(properties);
3612 }
3613
3614 // When applying paragraph styles dynamically, don't change the text objects' attributes
3615 // since they will computed as needed. Only apply the character styling if it's _only_
3616 // character styling. This policy is subject to change and might be put under user control.
3617
3618 // Hm. we might well be applying a mix of paragraph and character styles, in which
3619 // case we _do_ want to apply character styles regardless of what para styles are set.
3620 // But if we're applying a paragraph style, which has some character attributes, but
3621 // we only want the paragraphs to hold this character style, then we _don't_ want to
3622 // apply the character style. So we need to be able to choose.
3623
3624 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3625 {
3626 wxRichTextRange childRange(range);
3627 childRange.LimitTo(newPara->GetRange());
3628
3629 // Find the starting position and if necessary split it so
3630 // we can start applying different properties.
3631 // TODO: check that the properties actually change or are different
3632 // from properties outside of range
3633 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3634 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3635
3636 if (childRange.GetStart() == newPara->GetRange().GetStart())
3637 firstObject = newPara->GetChildren().GetFirst()->GetData();
3638 else
3639 firstObject = newPara->SplitAt(range.GetStart());
3640
3641 // Increment by 1 because we're apply the style one _after_ the split point
3642 long splitPoint = childRange.GetEnd();
3643 if (splitPoint != newPara->GetRange().GetEnd())
3644 splitPoint ++;
3645
3646 // Find last object
3647 if (splitPoint == newPara->GetRange().GetEnd())
3648 lastObject = newPara->GetChildren().GetLast()->GetData();
3649 else
3650 // lastObject is set as a side-effect of splitting. It's
3651 // returned as the object before the new object.
3652 (void) newPara->SplitAt(splitPoint, & lastObject);
3653
3654 wxASSERT(firstObject != NULL);
3655 wxASSERT(lastObject != NULL);
3656
3657 if (!firstObject || !lastObject)
3658 continue;
3659
3660 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3661 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3662
3663 wxASSERT(firstNode);
3664 wxASSERT(lastNode);
3665
3666 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3667
3668 while (node2)
3669 {
3670 wxRichTextObject* child = node2->GetData();
3671
3672 if (removeProperties)
3673 {
3674 // Removes the given properties from the paragraph
3675 child->GetProperties().RemoveProperties(properties);
3676 }
3677 else if (resetExistingProperties)
3678 child->GetProperties() = properties;
3679 else
3680 {
3681 child->GetProperties().MergeProperties(properties);
3682 }
3683
3684 if (node2 == lastNode)
3685 break;
3686
3687 node2 = node2->GetNext();
3688 }
3689 }
3690 }
3691 }
3692
3693 node = node->GetNext();
3694 }
3695
3696 // Do action, or delay it until end of batch.
3697 if (haveControl && withUndo)
3698 buffer->SubmitAction(action);
3699
3700 return true;
3701}
3702
5d7836c4
JS
3703void wxRichTextParagraphLayoutBox::Reset()
3704{
3705 Clear();
3706
603f702b
JS
3707 wxRichTextBuffer* buffer = GetBuffer();
3708 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3709 {
603f702b
JS
3710 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3711 event.SetEventObject(buffer->GetRichTextCtrl());
3712 event.SetContainer(this);
cd8ba0d9
JS
3713
3714 buffer->SendEvent(event, true);
3715 }
3716
7fe8059f 3717 AddParagraph(wxEmptyString);
3e541562 3718
cc2aecde
JS
3719 PrepareContent(*this);
3720
603f702b 3721 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3722}
3723
38113684
JS
3724/// Invalidate the buffer. With no argument, invalidates whole buffer.
3725void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3726{
603f702b 3727 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3728
603f702b
JS
3729 DoInvalidate(invalidRange);
3730}
3731
3732// Do the (in)validation for this object only
3733void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3734{
1e967276 3735 if (invalidRange == wxRICHTEXT_ALL)
38113684 3736 {
1e967276 3737 m_invalidRange = wxRICHTEXT_ALL;
38113684 3738 }
1e967276 3739 // Already invalidating everything
603f702b
JS
3740 else if (m_invalidRange == wxRICHTEXT_ALL)
3741 {
3742 }
3743 else
3744 {
3745 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3746 m_invalidRange.SetStart(invalidRange.GetStart());
3747 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3748 m_invalidRange.SetEnd(invalidRange.GetEnd());
3749 }
3750}
39a1c2f2 3751
603f702b
JS
3752// Do the (in)validation both up and down the hierarchy
3753void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3754{
3755 Invalidate(invalidRange);
3756
3757 if (invalidRange != wxRICHTEXT_NONE)
3758 {
3759 // Now go up the hierarchy
3760 wxRichTextObject* thisObj = this;
3761 wxRichTextObject* p = GetParent();
3762 while (p)
3763 {
3764 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3765 if (l)
3766 l->DoInvalidate(thisObj->GetRange());
3767
3768 thisObj = p;
3769 p = p->GetParent();
3770 }
3771 }
38113684
JS
3772}
3773
3774/// Get invalid range, rounding to entire paragraphs if argument is true.
3775wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3776{
1e967276 3777 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3778 return m_invalidRange;
39a1c2f2 3779
38113684 3780 wxRichTextRange range = m_invalidRange;
39a1c2f2 3781
38113684
JS
3782 if (wholeParagraphs)
3783 {
3784 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3785 if (para1)
3786 range.SetStart(para1->GetRange().GetStart());
cdaed652 3787 // floating layout make all child should be relayout
603f702b 3788 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3789 }
3790 return range;
3791}
3792
fe5aa22c
JS
3793/// Apply the style sheet to the buffer, for example if the styles have changed.
3794bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3795{
3796 wxASSERT(styleSheet != NULL);
3797 if (!styleSheet)
3798 return false;
3799
3800 int foundCount = 0;
3801
44580804
JS
3802 wxRichTextAttr attr(GetBasicStyle());
3803 if (GetBasicStyle().HasParagraphStyleName())
3804 {
3805 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3806 if (paraDef)
3807 {
3808 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3809 SetBasicStyle(attr);
3810 foundCount ++;
3811 }
3812 }
3813
3814 if (GetBasicStyle().HasCharacterStyleName())
3815 {
3816 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3817 if (charDef)
3818 {
3819 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3820 SetBasicStyle(attr);
3821 foundCount ++;
3822 }
3823 }
3824
fe5aa22c
JS
3825 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3826 while (node)
3827 {
3828 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3829 // wxASSERT (para != NULL);
fe5aa22c
JS
3830
3831 if (para)
3832 {
38f833b1
JS
3833 // Combine paragraph and list styles. If there is a list style in the original attributes,
3834 // the current indentation overrides anything else and is used to find the item indentation.
3835 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3836 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3837 // exception as above).
3838 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3839 // So when changing a list style interactively, could retrieve level based on current style, then
3840 // set appropriate indent and apply new style.
41a85215 3841
bbd55ff9
JS
3842 int outline = -1;
3843 int num = -1;
3844 if (para->GetAttributes().HasOutlineLevel())
3845 outline = para->GetAttributes().GetOutlineLevel();
3846 if (para->GetAttributes().HasBulletNumber())
3847 num = para->GetAttributes().GetBulletNumber();
3848
38f833b1
JS
3849 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3850 {
3851 int currentIndent = para->GetAttributes().GetLeftIndent();
3852
3853 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3854 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3855 if (paraDef && !listDef)
3856 {
336d8ae9 3857 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3858 foundCount ++;
3859 }
3860 else if (listDef && !paraDef)
3861 {
3862 // Set overall style defined for the list style definition
336d8ae9 3863 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3864
3865 // Apply the style for this level
3866 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3867 foundCount ++;
3868 }
3869 else if (listDef && paraDef)
3870 {
3871 // Combines overall list style, style for level, and paragraph style
336d8ae9 3872 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3873 foundCount ++;
3874 }
3875 }
3876 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3877 {
3878 int currentIndent = para->GetAttributes().GetLeftIndent();
3879
3880 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3881
41a85215 3882 // Overall list definition style
336d8ae9 3883 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3884
38f833b1
JS
3885 // Style for this level
3886 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3887
3888 foundCount ++;
3889 }
3890 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3891 {
3892 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3893 if (def)
3894 {
336d8ae9 3895 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3896 foundCount ++;
3897 }
3898 }
bbd55ff9
JS
3899
3900 if (outline != -1)
3901 para->GetAttributes().SetOutlineLevel(outline);
3902 if (num != -1)
3903 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3904 }
3905
3906 node = node->GetNext();
3907 }
3908 return foundCount != 0;
3909}
3910
38f833b1
JS
3911/// Set list style
3912bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3913{
603f702b
JS
3914 wxRichTextBuffer* buffer = GetBuffer();
3915 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 3916
38f833b1
JS
3917 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3918 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3919 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3920 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 3921
38f833b1
JS
3922 // Current number, if numbering
3923 int n = startFrom;
41a85215 3924
38f833b1
JS
3925 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3926
3927 // If we are associated with a control, make undoable; otherwise, apply immediately
3928 // to the data.
3929
603f702b 3930 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
3931
3932 wxRichTextAction* action = NULL;
3933
3934 if (haveControl && withUndo)
3935 {
603f702b 3936 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 3937 action->SetRange(range);
603f702b 3938 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
3939 }
3940
3941 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3942 while (node)
3943 {
3944 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3945 // wxASSERT (para != NULL);
38f833b1
JS
3946
3947 if (para && para->GetChildCount() > 0)
3948 {
3949 // Stop searching if we're beyond the range of interest
3950 if (para->GetRange().GetStart() > range.GetEnd())
3951 break;
3952
3953 if (!para->GetRange().IsOutside(range))
3954 {
3955 // We'll be using a copy of the paragraph to make style changes,
3956 // not updating the buffer directly.
3957 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3958
3959 if (haveControl && withUndo)
3960 {
3961 newPara = new wxRichTextParagraph(*para);
3962 action->GetNewParagraphs().AppendChild(newPara);
3963
3964 // Also store the old ones for Undo
3965 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3966 }
3967 else
3968 newPara = para;
41a85215 3969
38f833b1
JS
3970 if (def)
3971 {
3972 int thisIndent = newPara->GetAttributes().GetLeftIndent();
3973 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 3974
38f833b1
JS
3975 // How is numbering going to work?
3976 // If we are renumbering, or numbering for the first time, we need to keep
3977 // track of the number for each level. But we might be simply applying a different
3978 // list style.
3979 // In Word, applying a style to several paragraphs, even if at different levels,
3980 // reverts the level back to the same one. So we could do the same here.
3981 // Renumbering will need to be done when we promote/demote a paragraph.
3982
3983 // Apply the overall list style, and item style for this level
24777478 3984 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 3985 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 3986
d2d0adc7 3987 // Now we need to do numbering
38f833b1
JS
3988 if (renumber)
3989 {
3990 newPara->GetAttributes().SetBulletNumber(n);
3991 }
41a85215 3992
38f833b1
JS
3993 n ++;
3994 }
3995 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
3996 {
3997 // if def is NULL, remove list style, applying any associated paragraph style
3998 // to restore the attributes
3999
4000 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4001 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 4002 newPara->GetAttributes().SetBulletText(wxEmptyString);
41a85215 4003
38f833b1 4004 // Eliminate the main list-related attributes
d2d0adc7 4005 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 4006
38f833b1
JS
4007 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4008 {
4009 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4010 if (def)
4011 {
336d8ae9 4012 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
4013 }
4014 }
4015 }
4016 }
4017 }
4018
4019 node = node->GetNext();
4020 }
4021
4022 // Do action, or delay it until end of batch.
4023 if (haveControl && withUndo)
603f702b 4024 buffer->SubmitAction(action);
38f833b1
JS
4025
4026 return true;
4027}
4028
4029bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4030{
603f702b
JS
4031 wxRichTextBuffer* buffer = GetBuffer();
4032 if (buffer && buffer->GetStyleSheet())
38f833b1 4033 {
603f702b 4034 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4035 if (def)
4036 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4037 }
4038 return false;
4039}
4040
4041/// Clear list for given range
4042bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4043{
4044 return SetListStyle(range, NULL, flags);
4045}
4046
4047/// Number/renumber any list elements in the given range
4048bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4049{
4050 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4051}
4052
4053/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4054bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4055 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4056{
603f702b
JS
4057 wxRichTextBuffer* buffer = GetBuffer();
4058 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4059
38f833b1
JS
4060 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4061 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 4062#if wxDEBUG_LEVEL
38f833b1 4063 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 4064#endif
38f833b1
JS
4065
4066 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4067
38f833b1
JS
4068 // Max number of levels
4069 const int maxLevels = 10;
41a85215 4070
38f833b1
JS
4071 // The level we're looking at now
4072 int currentLevel = -1;
41a85215 4073
38f833b1
JS
4074 // The item number for each level
4075 int levels[maxLevels];
4076 int i;
41a85215 4077
38f833b1
JS
4078 // Reset all numbering
4079 for (i = 0; i < maxLevels; i++)
4080 {
4081 if (startFrom != -1)
d2d0adc7 4082 levels[i] = startFrom-1;
38f833b1 4083 else if (renumber) // start again
d2d0adc7 4084 levels[i] = 0;
38f833b1
JS
4085 else
4086 levels[i] = -1; // start from the number we found, if any
4087 }
41a85215 4088
bb7bbd12 4089#if wxDEBUG_LEVEL
38f833b1 4090 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
bb7bbd12 4091#endif
38f833b1
JS
4092
4093 // If we are associated with a control, make undoable; otherwise, apply immediately
4094 // to the data.
4095
603f702b 4096 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4097
4098 wxRichTextAction* action = NULL;
4099
4100 if (haveControl && withUndo)
4101 {
603f702b 4102 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4103 action->SetRange(range);
603f702b 4104 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4105 }
4106
4107 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4108 while (node)
4109 {
4110 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4111 // wxASSERT (para != NULL);
38f833b1
JS
4112
4113 if (para && para->GetChildCount() > 0)
4114 {
4115 // Stop searching if we're beyond the range of interest
4116 if (para->GetRange().GetStart() > range.GetEnd())
4117 break;
4118
4119 if (!para->GetRange().IsOutside(range))
4120 {
4121 // We'll be using a copy of the paragraph to make style changes,
4122 // not updating the buffer directly.
4123 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4124
4125 if (haveControl && withUndo)
4126 {
4127 newPara = new wxRichTextParagraph(*para);
4128 action->GetNewParagraphs().AppendChild(newPara);
4129
4130 // Also store the old ones for Undo
4131 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4132 }
4133 else
4134 newPara = para;
41a85215 4135
38f833b1
JS
4136 wxRichTextListStyleDefinition* defToUse = def;
4137 if (!defToUse)
4138 {
336d8ae9
VZ
4139 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4140 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 4141 }
41a85215 4142
38f833b1
JS
4143 if (defToUse)
4144 {
4145 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4146 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4147
d2d0adc7
JS
4148 // If we've specified a level to apply to all, change the level.
4149 if (specifiedLevel != -1)
38f833b1 4150 thisLevel = specifiedLevel;
41a85215 4151
38f833b1
JS
4152 // Do promotion if specified
4153 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4154 {
4155 thisLevel = thisLevel - promoteBy;
4156 if (thisLevel < 0)
4157 thisLevel = 0;
4158 if (thisLevel > 9)
4159 thisLevel = 9;
4160 }
41a85215 4161
38f833b1 4162 // Apply the overall list style, and item style for this level
24777478 4163 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4164 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4165
38f833b1 4166 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 4167
38f833b1
JS
4168 if (currentLevel == -1)
4169 currentLevel = thisLevel;
41a85215 4170
38f833b1
JS
4171 // Same level as before, do nothing except increment level's number afterwards
4172 if (currentLevel == thisLevel)
4173 {
4174 }
4175 // A deeper level: start renumbering all levels after current level
4176 else if (thisLevel > currentLevel)
4177 {
4178 for (i = currentLevel+1; i <= thisLevel; i++)
4179 {
d2d0adc7 4180 levels[i] = 0;
38f833b1
JS
4181 }
4182 currentLevel = thisLevel;
4183 }
4184 else if (thisLevel < currentLevel)
4185 {
4186 currentLevel = thisLevel;
41a85215 4187 }
38f833b1
JS
4188
4189 // Use the current numbering if -1 and we have a bullet number already
4190 if (levels[currentLevel] == -1)
4191 {
4192 if (newPara->GetAttributes().HasBulletNumber())
4193 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4194 else
4195 levels[currentLevel] = 1;
4196 }
d2d0adc7
JS
4197 else
4198 {
4199 levels[currentLevel] ++;
4200 }
41a85215 4201
38f833b1
JS
4202 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4203
d2d0adc7
JS
4204 // Create the bullet text if an outline list
4205 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4206 {
4207 wxString text;
4208 for (i = 0; i <= currentLevel; i++)
4209 {
4210 if (!text.IsEmpty())
4211 text += wxT(".");
4212 text += wxString::Format(wxT("%d"), levels[i]);
4213 }
4214 newPara->GetAttributes().SetBulletText(text);
4215 }
38f833b1
JS
4216 }
4217 }
4218 }
4219
4220 node = node->GetNext();
4221 }
4222
4223 // Do action, or delay it until end of batch.
4224 if (haveControl && withUndo)
603f702b 4225 buffer->SubmitAction(action);
38f833b1
JS
4226
4227 return true;
4228}
4229
4230bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4231{
603f702b
JS
4232 wxRichTextBuffer* buffer = GetBuffer();
4233 if (buffer->GetStyleSheet())
38f833b1
JS
4234 {
4235 wxRichTextListStyleDefinition* def = NULL;
4236 if (!defName.IsEmpty())
603f702b 4237 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4238 return NumberList(range, def, flags, startFrom, specifiedLevel);
4239 }
4240 return false;
4241}
4242
4243/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4244bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4245{
4246 // TODO
4247 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4248 // to NumberList with a flag indicating promotion is required within one of the ranges.
4249 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4250 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4251 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4252 // list position will start from 1.
4253 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4254 // We can end the renumbering at this point.
41a85215 4255
38f833b1 4256 // For now, only renumber within the promotion range.
41a85215 4257
38f833b1
JS
4258 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4259}
4260
4261bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4262{
603f702b
JS
4263 wxRichTextBuffer* buffer = GetBuffer();
4264 if (buffer->GetStyleSheet())
38f833b1
JS
4265 {
4266 wxRichTextListStyleDefinition* def = NULL;
4267 if (!defName.IsEmpty())
603f702b 4268 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4269 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4270 }
4271 return false;
4272}
4273
d2d0adc7
JS
4274/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4275/// position of the paragraph that it had to start looking from.
24777478 4276bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 4277{
d2d0adc7
JS
4278 if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4279 return false;
3e541562 4280
603f702b
JS
4281 wxRichTextBuffer* buffer = GetBuffer();
4282 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4283 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 4284 {
336d8ae9 4285 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
4286 if (def)
4287 {
4288 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4289 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4290
d2d0adc7
JS
4291 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4292
4293 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4294 if (previousParagraph->GetAttributes().HasBulletName())
4295 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4296 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4297 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4298
d2d0adc7
JS
4299 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4300 attr.SetBulletNumber(nextNumber);
3e541562 4301
d2d0adc7
JS
4302 if (isOutline)
4303 {
4304 wxString text = previousParagraph->GetAttributes().GetBulletText();
4305 if (!text.IsEmpty())
4306 {
4307 int pos = text.Find(wxT('.'), true);
4308 if (pos != wxNOT_FOUND)
4309 {
4310 text = text.Mid(0, text.Length() - pos - 1);
4311 }
4312 else
4313 text = wxEmptyString;
4314 if (!text.IsEmpty())
4315 text += wxT(".");
4316 text += wxString::Format(wxT("%d"), nextNumber);
4317 attr.SetBulletText(text);
4318 }
4319 }
3e541562 4320
d2d0adc7
JS
4321 return true;
4322 }
4323 else
4324 return false;
4325 }
4326 else
4327 return false;
4328}
4329
5d7836c4
JS
4330/*!
4331 * wxRichTextParagraph
4332 * This object represents a single paragraph (or in a straight text editor, a line).
4333 */
4334
603f702b 4335IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4336
cfa3b256
JS
4337wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4338
24777478 4339wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4340 wxRichTextCompositeObject(parent)
5d7836c4 4341{
5d7836c4
JS
4342 if (style)
4343 SetAttributes(*style);
4344}
4345
24777478 4346wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4347 wxRichTextCompositeObject(parent)
5d7836c4 4348{
4f32b3cf
JS
4349 if (paraStyle)
4350 SetAttributes(*paraStyle);
5d7836c4 4351
4f32b3cf 4352 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4353}
4354
4355wxRichTextParagraph::~wxRichTextParagraph()
4356{
4357 ClearLines();
4358}
4359
4360/// Draw the item
8db2e3ef 4361bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4362{
603f702b
JS
4363 if (!IsShown())
4364 return true;
4365
4366 // Currently we don't merge these attributes with the parent, but we
4367 // should consider whether we should (e.g. if we set a border colour
4368 // for all paragraphs). But generally box attributes are likely to be
4369 // different for different objects.
4370 wxRect paraRect = GetRect();
24777478 4371 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef
JS
4372 context.ApplyVirtualAttributes(attr, this);
4373
4374 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
fe5aa22c 4375
5d7836c4 4376 // Draw the bullet, if any
fe5aa22c 4377 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4 4378 {
fe5aa22c 4379 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4380 {
fe5aa22c 4381 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4382 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4383
8db2e3ef 4384 wxRichTextAttr bulletAttr(attr);
d2d0adc7 4385
e3eac0ff
JS
4386 // Combine with the font of the first piece of content, if one is specified
4387 if (GetChildren().GetCount() > 0)
4388 {
4389 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4390 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4391 {
4392 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4393 }
4394 }
4395
d2d0adc7 4396 // Get line height from first line, if any
d3b9f782 4397 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4398
4399 wxPoint linePos;
4400 int lineHeight wxDUMMY_INITIALIZE(0);
4401 if (line)
5d7836c4 4402 {
d2d0adc7
JS
4403 lineHeight = line->GetSize().y;
4404 linePos = line->GetPosition() + GetPosition();
5d7836c4 4405 }
d2d0adc7 4406 else
f089713f 4407 {
f089713f 4408 wxFont font;
44cc96a8
JS
4409 if (bulletAttr.HasFont() && GetBuffer())
4410 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4411 else
4412 font = (*wxNORMAL_FONT);
4413
ecb5fbf1 4414 wxCheckSetFont(dc, font);
f089713f 4415
d2d0adc7
JS
4416 lineHeight = dc.GetCharHeight();
4417 linePos = GetPosition();
4418 linePos.y += spaceBeforePara;
4419 }
f089713f 4420
d2d0adc7 4421 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4422
d2d0adc7
JS
4423 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4424 {
4425 if (wxRichTextBuffer::GetRenderer())
4426 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4427 }
3e541562
JS
4428 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4429 {
d2d0adc7
JS
4430 if (wxRichTextBuffer::GetRenderer())
4431 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4432 }
5d7836c4
JS
4433 else
4434 {
4435 wxString bulletText = GetBulletText();
3e541562 4436
d2d0adc7
JS
4437 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4438 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4439 }
4440 }
4441 }
7fe8059f 4442
5d7836c4
JS
4443 // Draw the range for each line, one object at a time.
4444
4445 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4446 while (node)
4447 {
4448 wxRichTextLine* line = node->GetData();
1e967276 4449 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4450
5d7836c4
JS
4451 // Lines are specified relative to the paragraph
4452
4453 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4454
7051fa41
JS
4455 // Don't draw if off the screen
4456 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4457 {
7051fa41
JS
4458 wxPoint objectPosition = linePosition;
4459 int maxDescent = line->GetDescent();
4460
4461 // Loop through objects until we get to the one within range
4462 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4463
7051fa41
JS
4464 int i = 0;
4465 while (node2)
5d7836c4 4466 {
7051fa41 4467 wxRichTextObject* child = node2->GetData();
5d7836c4 4468
cdaed652 4469 if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4470 {
7051fa41
JS
4471 // Draw this part of the line at the correct position
4472 wxRichTextRange objectRange(child->GetRange());
4473 objectRange.LimitTo(lineRange);
4474
4475 wxSize objectSize;
603f702b 4476 if (child->IsTopLevel())
7051fa41 4477 {
603f702b
JS
4478 objectSize = child->GetCachedSize();
4479 objectRange = child->GetOwnRange();
7051fa41
JS
4480 }
4481 else
7051fa41 4482 {
603f702b
JS
4483#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4484 if (i < (int) line->GetObjectSizes().GetCount())
4485 {
4486 objectSize.x = line->GetObjectSizes()[(size_t) i];
4487 }
4488 else
4489#endif
4490 {
4491 int descent = 0;
8db2e3ef 4492 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
603f702b 4493 }
7051fa41 4494 }
5d7836c4 4495
7051fa41
JS
4496 // Use the child object's width, but the whole line's height
4497 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
8db2e3ef 4498 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4499
7051fa41
JS
4500 objectPosition.x += objectSize.x;
4501 i ++;
4502 }
4503 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4504 // Can break out of inner loop now since we've passed this line's range
4505 break;
5d7836c4 4506
7051fa41
JS
4507 node2 = node2->GetNext();
4508 }
5d7836c4
JS
4509 }
4510
4511 node = node->GetNext();
7fe8059f 4512 }
5d7836c4
JS
4513
4514 return true;
4515}
4516
4f3d5bc0
JS
4517// Get the range width using partial extents calculated for the whole paragraph.
4518static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4519{
4520 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4521
affbfa1f
JS
4522 if (partialExtents.GetCount() < (size_t) range.GetLength())
4523 return 0;
4524
4f3d5bc0
JS
4525 int leftMostPos = 0;
4526 if (range.GetStart() - para.GetRange().GetStart() > 0)
4527 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4528
4529 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4530
4531 int w = rightMostPos - leftMostPos;
4532
4533 return w;
4534}
4535
5d7836c4 4536/// Lay the item out
8db2e3ef 4537bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 4538{
cdaed652
VZ
4539 // Deal with floating objects firstly before the normal layout
4540 wxRichTextBuffer* buffer = GetBuffer();
4541 wxASSERT(buffer);
07d4142f 4542 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
cdaed652 4543 wxASSERT(collector);
8db2e3ef 4544 LayoutFloat(dc, context, rect, style, collector);
cdaed652 4545
24777478 4546 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef 4547 context.ApplyVirtualAttributes(attr, this);
fe5aa22c 4548
169adfa9
JS
4549 // ClearLines();
4550
5d7836c4 4551 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4552 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4553 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4554 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4555 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4556 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4557
4558 int lineSpacing = 0;
4559
4560 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
a1b806b9 4561 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
5d7836c4 4562 {
8f0e4366
JS
4563 wxCheckSetFont(dc, attr.GetFont());
4564 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
5d7836c4
JS
4565 }
4566
5d7836c4
JS
4567 // Start position for each line relative to the paragraph
4568 int startPositionFirstLine = leftIndent;
4569 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4570
4571 // If we have a bullet in this paragraph, the start position for the first line's text
4572 // is actually leftIndent + leftSubIndent.
fe5aa22c 4573 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4574 startPositionFirstLine = startPositionSubsequentLines;
4575
5d7836c4
JS
4576 long lastEndPos = GetRange().GetStart()-1;
4577 long lastCompletedEndPos = lastEndPos;
4578
4579 int currentWidth = 0;
4580 SetPosition(rect.GetPosition());
4581
4582 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4583 int lineHeight = 0;
4584 int maxWidth = 0;
603f702b 4585 int maxHeight = currentPosition.y;
476a319a 4586 int maxAscent = 0;
5d7836c4 4587 int maxDescent = 0;
5d7836c4 4588 int lineCount = 0;
cdaed652
VZ
4589 int lineAscent = 0;
4590 int lineDescent = 0;
5d7836c4 4591
2f45f554
JS
4592 wxRichTextObjectList::compatibility_iterator node;
4593
4594#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4595 wxUnusedVar(style);
4596 wxArrayInt partialExtents;
4597
4598 wxSize paraSize;
8aab23a1 4599 int paraDescent = 0;
2f45f554
JS
4600
4601 // This calculates the partial text extents
8db2e3ef 4602 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
2f45f554
JS
4603#else
4604 node = m_children.GetFirst();
ecb5fbf1
JS
4605 while (node)
4606 {
4607 wxRichTextObject* child = node->GetData();
4608
603f702b 4609 //child->SetCachedSize(wxDefaultSize);
8db2e3ef 4610 child->Layout(dc, context, rect, style);
ecb5fbf1
JS
4611
4612 node = node->GetNext();
4613 }
31778480
JS
4614#endif
4615
5d7836c4
JS
4616 // Split up lines
4617
4618 // We may need to go back to a previous child, in which case create the new line,
4619 // find the child corresponding to the start position of the string, and
4620 // continue.
4621
603f702b
JS
4622 wxRect availableRect;
4623
ecb5fbf1 4624 node = m_children.GetFirst();
5d7836c4
JS
4625 while (node)
4626 {
4627 wxRichTextObject* child = node->GetData();
4628
cdaed652 4629 // If floating, ignore. We already laid out floats.
603f702b
JS
4630 // Also ignore if empty object, except if we haven't got any
4631 // size yet.
4632 if (child->IsFloating() || !child->IsShown() ||
4633 (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4634 )
affbfa1f
JS
4635 {
4636 node = node->GetNext();
4637 continue;
4638 }
4639
5d7836c4
JS
4640 // If this is e.g. a composite text box, it will need to be laid out itself.
4641 // But if just a text fragment or image, for example, this will
4642 // do nothing. NB: won't we need to set the position after layout?
4643 // since for example if position is dependent on vertical line size, we
4644 // can't tell the position until the size is determined. So possibly introduce
4645 // another layout phase.
4646
5d7836c4
JS
4647 // We may only be looking at part of a child, if we searched back for wrapping
4648 // and found a suitable point some way into the child. So get the size for the fragment
4649 // if necessary.
3e541562 4650
ff76711f
JS
4651 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4652 long lastPosToUse = child->GetRange().GetEnd();
4653 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4654
ff76711f
JS
4655 if (lineBreakInThisObject)
4656 lastPosToUse = nextBreakPos;
5d7836c4
JS
4657
4658 wxSize childSize;
4659 int childDescent = 0;
3e541562 4660
603f702b
JS
4661 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4662 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4663 rect.width - startOffset - rightIndent, rect.height);
4664
4665 if (child->IsTopLevel())
4666 {
4667 wxSize oldSize = child->GetCachedSize();
4668
4669 child->Invalidate(wxRICHTEXT_ALL);
4670 child->SetPosition(wxPoint(0, 0));
4671
4672 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4673 // lays out the object again using the minimum size
4674 // The position will be determined by its location in its line,
4675 // and not by the child's actual position.
8db2e3ef
JS
4676 child->LayoutToBestSize(dc, context, buffer,
4677 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4678
4679 if (oldSize != child->GetCachedSize())
4680 {
4681 partialExtents.Clear();
4682
4683 // Recalculate the partial text extents since the child object changed size
8db2e3ef 4684 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
603f702b
JS
4685 }
4686 }
4687
4688 // Problem: we need to layout composites here for which we need the available width,
4689 // but we can't get the available width without using the float collector which
4690 // needs to know the object height.
4691
ff76711f 4692 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4693 {
4694 childSize = child->GetCachedSize();
4695 childDescent = child->GetDescent();
4696 }
4697 else
4f3d5bc0
JS
4698 {
4699#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4700 // Get height only, then the width using the partial extents
8db2e3ef 4701 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4f3d5bc0
JS
4702 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4703#else
8db2e3ef 4704 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
4f3d5bc0
JS
4705#endif
4706 }
ff76711f 4707
603f702b
JS
4708 bool doLoop = true;
4709 int loopIterations = 0;
4710
4711 // If there are nested objects that need to lay themselves out, we have to do this in a
4712 // loop because the height of the object may well depend on the available width.
4713 // And because of floating object positioning, the available width depends on the
4714 // height of the object and whether it will clash with the floating objects.
4715 // So, we see whether the available width changes due to the presence of floating images.
4716 // If it does, then we'll use the new restricted width to find the object height again.
4717 // If this causes another restriction in the available width, we'll try again, until
4718 // either we lose patience or the available width settles down.
4719 do
4720 {
4721 loopIterations ++;
4722
4723 wxRect oldAvailableRect = availableRect;
4724
4725 // Available width depends on the floating objects and the line height.
7c9fdebe 4726 // Note: the floating objects may be placed vertically along the two sides of
603f702b
JS
4727 // buffer, so we may have different available line widths with different
4728 // [startY, endY]. So, we can't determine how wide the available
4729 // space is until we know the exact line height.
a70eb13e
JS
4730 if (childDescent == 0)
4731 {
4732 lineHeight = wxMax(lineHeight, childSize.y);
4733 lineDescent = maxDescent;
4734 lineAscent = maxAscent;
4735 }
4736 else
4737 {
4738 lineDescent = wxMax(childDescent, maxDescent);
4739 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4740 }
4741 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
603f702b
JS
4742 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4743
4744 // Adjust availableRect to the space that is available when taking floating objects into account.
4745
4746 if (floatAvailableRect.x + startOffset > availableRect.x)
4747 {
4748 int newX = floatAvailableRect.x + startOffset;
4749 int newW = availableRect.width - (newX - availableRect.x);
4750 availableRect.x = newX;
4751 availableRect.width = newW;
4752 }
4753
4754 if (floatAvailableRect.width < availableRect.width)
4755 availableRect.width = floatAvailableRect.width;
4756
4757 currentPosition.x = availableRect.x - rect.x;
4758
4759 if (child->IsTopLevel() && loopIterations <= 20)
4760 {
4761 if (availableRect != oldAvailableRect)
4762 {
4763 wxSize oldSize = child->GetCachedSize();
4764
603f702b
JS
4765 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4766 // lays out the object again using the minimum size
4767 child->Invalidate(wxRICHTEXT_ALL);
8db2e3ef
JS
4768 child->LayoutToBestSize(dc, context, buffer,
4769 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4770 childSize = child->GetCachedSize();
4771 childDescent = child->GetDescent();
603f702b
JS
4772
4773 if (oldSize != child->GetCachedSize())
4774 {
4775 partialExtents.Clear();
4776
4777 // Recalculate the partial text extents since the child object changed size
8db2e3ef 4778 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
603f702b 4779 }
cdaed652 4780
603f702b
JS
4781 // Go around the loop finding the available rect for the given floating objects
4782 }
4783 else
4784 doLoop = false;
4785 }
4786 else
4787 doLoop = false;
4788 }
4789 while (doLoop);
cdaed652 4790
20d09da5
JS
4791 if (child->IsTopLevel())
4792 {
4793 // We can move it to the correct position at this point
32423dd8
JS
4794 // TODO: probably need to add margin
4795 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
20d09da5
JS
4796 }
4797
ff76711f
JS
4798 // Cases:
4799 // 1) There was a line break BEFORE the natural break
4800 // 2) There was a line break AFTER the natural break
603f702b
JS
4801 // 3) It's the last line
4802 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4803
603f702b
JS
4804 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4805 ||
4806 (childSize.x + currentWidth > availableRect.width)
4807 ||
4808 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4809
4810 )
5d7836c4
JS
4811 {
4812 long wrapPosition = 0;
603f702b
JS
4813 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4814 wrapPosition = child->GetRange().GetEnd();
4815 else
5d7836c4
JS
4816
4817 // Find a place to wrap. This may walk back to previous children,
4818 // for example if a word spans several objects.
cdaed652
VZ
4819 // Note: one object must contains only one wxTextAtrr, so the line height will not
4820 // change inside one object. Thus, we can pass the remain line width to the
4821 // FindWrapPosition function.
8db2e3ef 4822 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4823 {
4824 // If the function failed, just cut it off at the end of this child.
4825 wrapPosition = child->GetRange().GetEnd();
4826 }
4827
4828 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4829 if (wrapPosition <= lastCompletedEndPos)
4830 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4831
603f702b
JS
4832 // Line end position shouldn't be the same as the end, or greater.
4833 if (wrapPosition >= GetRange().GetEnd())
4834 wrapPosition = GetRange().GetEnd()-1;
4835
5d7836c4 4836 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4837
5d7836c4
JS
4838 // Let's find the actual size of the current line now
4839 wxSize actualSize;
4840 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4841
a70eb13e 4842 childDescent = 0;
4ab8a5e2 4843
4f3d5bc0 4844#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4845 if (!child->IsEmpty())
4846 {
4847 // Get height only, then the width using the partial extents
8db2e3ef 4848 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
603f702b
JS
4849 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4850 }
4851 else
4f3d5bc0 4852#endif
8db2e3ef 4853 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
4f3d5bc0 4854
5d7836c4 4855 currentWidth = actualSize.x;
a70eb13e
JS
4856
4857 // The descent for the whole line at this point, is the correct max descent
4858 maxDescent = childDescent;
4859 // Maximum ascent
4860 maxAscent = actualSize.y-childDescent;
4861
4862 // lineHeight is given by the height for the whole line, since it will
4863 // take into account ascend/descend.
4864 lineHeight = actualSize.y;
7fe8059f 4865
07d4142f 4866 if (lineHeight == 0 && buffer)
603f702b 4867 {
07d4142f 4868 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
4869 wxCheckSetFont(dc, font);
4870 lineHeight = dc.GetCharHeight();
4871 }
4872
4873 if (maxDescent == 0)
4874 {
4875 int w, h;
4876 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
4877 }
4878
5d7836c4 4879 // Add a new line
1e967276 4880 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 4881
1e967276
JS
4882 // Set relative range so we won't have to change line ranges when paragraphs are moved
4883 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
4884 line->SetPosition(currentPosition);
4885 line->SetSize(wxSize(currentWidth, lineHeight));
4886 line->SetDescent(maxDescent);
4887
603f702b
JS
4888 maxHeight = currentPosition.y + lineHeight;
4889
5d7836c4
JS
4890 // Now move down a line. TODO: add margins, spacing
4891 currentPosition.y += lineHeight;
4892 currentPosition.y += lineSpacing;
5d7836c4 4893 maxDescent = 0;
476a319a 4894 maxAscent = 0;
603f702b
JS
4895 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4896 currentWidth = 0;
7fe8059f 4897
5d7836c4
JS
4898 lineCount ++;
4899
a70eb13e 4900 // TODO: account for zero-length objects
603f702b 4901 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 4902
5d7836c4
JS
4903 lastEndPos = wrapPosition;
4904 lastCompletedEndPos = lastEndPos;
4905
4906 lineHeight = 0;
4907
603f702b
JS
4908 if (wrapPosition < GetRange().GetEnd()-1)
4909 {
4910 // May need to set the node back to a previous one, due to searching back in wrapping
4911 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
4912 if (childAfterWrapPosition)
4913 node = m_children.Find(childAfterWrapPosition);
4914 else
4915 node = node->GetNext();
4916 }
5d7836c4
JS
4917 else
4918 node = node->GetNext();
603f702b
JS
4919
4920 // Apply paragraph styles such as alignment to the wrapped line
4921 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
4922 }
4923 else
4924 {
4925 // We still fit, so don't add a line, and keep going
4926 currentWidth += childSize.x;
a70eb13e
JS
4927
4928 if (childDescent == 0)
4929 {
4930 // An object with a zero descend value wants to take up the whole
4931 // height regardless of baseline
4932 lineHeight = wxMax(lineHeight, childSize.y);
4933 }
4934 else
4935 {
4936 maxDescent = wxMax(childDescent, maxDescent);
4937 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
4938 }
4939
4940 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5d7836c4 4941
603f702b 4942 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
4943 lastEndPos = child->GetRange().GetEnd();
4944
4945 node = node->GetNext();
4946 }
4947 }
4948
07d4142f 4949 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 4950
1e967276
JS
4951 // Remove remaining unused line objects, if any
4952 ClearUnusedLines(lineCount);
4953
603f702b
JS
4954 // We need to add back the margins etc.
4955 {
4956 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4957 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 4958 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4959 SetCachedSize(marginRect.GetSize());
4960 }
4961
4962 // The maximum size is the length of the paragraph stretched out into a line.
4963 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4964 // this size. TODO: take into account line breaks.
4965 {
4966 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 4967 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 4968 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4969 SetMaxSize(marginRect.GetSize());
4970 }
4971
4972 // Find the greatest minimum size. Currently we only look at non-text objects,
4973 // which isn't ideal but it would be slow to find the maximum word width to
4974 // use as the minimum.
4975 {
4976 int minWidth = 0;
4977 node = m_children.GetFirst();
4978 while (node)
4979 {
4980 wxRichTextObject* child = node->GetData();
4981
4982 // If floating, ignore. We already laid out floats.
4983 // Also ignore if empty object, except if we haven't got any
4984 // size yet.
345c78ca 4985 if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
603f702b
JS
4986 {
4987 if (child->GetCachedSize().x > minWidth)
4988 minWidth = child->GetMinSize().x;
4989 }
4990 node = node->GetNext();
4991 }
5d7836c4 4992
603f702b
JS
4993 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4994 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 4995 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4996 SetMinSize(marginRect.GetSize());
4997 }
5d7836c4 4998
2f45f554
JS
4999#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5000#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5001 // Use the text extents to calculate the size of each fragment in each line
5002 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5003 while (lineNode)
5004 {
5005 wxRichTextLine* line = lineNode->GetData();
5006 wxRichTextRange lineRange = line->GetAbsoluteRange();
5007
5008 // Loop through objects until we get to the one within range
5009 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5010
5011 while (node2)
5012 {
5013 wxRichTextObject* child = node2->GetData();
5014
affbfa1f 5015 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
5016 {
5017 wxRichTextRange rangeToUse = lineRange;
5018 rangeToUse.LimitTo(child->GetRange());
5019
5020 // Find the size of the child from the text extents, and store in an array
5021 // for drawing later
5022 int left = 0;
5023 if (rangeToUse.GetStart() > GetRange().GetStart())
5024 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5025 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5026 int sz = right - left;
5027 line->GetObjectSizes().Add(sz);
5028 }
5029 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5030 // Can break out of inner loop now since we've passed this line's range
5031 break;
5032
5033 node2 = node2->GetNext();
5034 }
5035
5036 lineNode = lineNode->GetNext();
5037 }
5038#endif
5039#endif
5040
5d7836c4
JS
5041 return true;
5042}
5043
603f702b
JS
5044/// Apply paragraph styles, such as centering, to wrapped lines
5045/// TODO: take into account box attributes, possibly
5046void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5047{
5048 if (!attr.HasAlignment())
5049 return;
5050
5051 wxPoint pos = line->GetPosition();
32423dd8 5052 wxPoint originalPos = pos;
603f702b
JS
5053 wxSize size = line->GetSize();
5054
5055 // centering, right-justification
8db2e3ef 5056 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5057 {
5058 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5059 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5060 line->SetPosition(pos);
5061 }
8db2e3ef 5062 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5063 {
5064 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5065 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5066 line->SetPosition(pos);
5067 }
32423dd8
JS
5068
5069 if (pos != originalPos)
5070 {
5071 wxPoint inc = pos - originalPos;
5072
5073 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5074
5075 while (node)
5076 {
5077 wxRichTextObject* child = node->GetData();
5078 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5079 child->Move(child->GetPosition() + inc);
5080
5081 node = node->GetNext();
5082 }
5083 }
603f702b 5084}
5d7836c4
JS
5085
5086/// Insert text at the given position
5087bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5088{
5089 wxRichTextObject* childToUse = NULL;
09f14108 5090 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5091
5092 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5093 while (node)
5094 {
5095 wxRichTextObject* child = node->GetData();
5096 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5097 {
5098 childToUse = child;
5099 nodeToUse = node;
5100 break;
5101 }
5102
5103 node = node->GetNext();
5104 }
5105
5106 if (childToUse)
5107 {
5108 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5109 if (textObject)
5110 {
5111 int posInString = pos - textObject->GetRange().GetStart();
5112
5113 wxString newText = textObject->GetText().Mid(0, posInString) +
5114 text + textObject->GetText().Mid(posInString);
5115 textObject->SetText(newText);
5116
28f92d74 5117 int textLength = text.length();
5d7836c4
JS
5118
5119 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5120 textObject->GetRange().GetEnd() + textLength));
5121
5122 // Increment the end range of subsequent fragments in this paragraph.
5123 // We'll set the paragraph range itself at a higher level.
5124
5125 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5126 while (node)
5127 {
5128 wxRichTextObject* child = node->GetData();
5129 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5130 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5131
5d7836c4
JS
5132 node = node->GetNext();
5133 }
5134
5135 return true;
5136 }
5137 else
5138 {
5139 // TODO: if not a text object, insert at closest position, e.g. in front of it
5140 }
5141 }
5142 else
5143 {
5144 // Add at end.
5145 // Don't pass parent initially to suppress auto-setting of parent range.
5146 // We'll do that at a higher level.
5147 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5148
5149 AppendChild(textObject);
5150 return true;
5151 }
5152
5153 return false;
5154}
5155
5156void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5157{
bec80f4f 5158 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5159}
5160
5161/// Clear the cached lines
5162void wxRichTextParagraph::ClearLines()
5163{
5164 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5165}
5166
5167/// Get/set the object size for the given range. Returns false if the range
5168/// is invalid for this object.
8db2e3ef 5169bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
5170{
5171 if (!range.IsWithin(GetRange()))
5172 return false;
5173
5174 if (flags & wxRICHTEXT_UNFORMATTED)
5175 {
5176 // Just use unformatted data, assume no line breaks
5d7836c4
JS
5177 wxSize sz;
5178
31778480
JS
5179 wxArrayInt childExtents;
5180 wxArrayInt* p;
5181 if (partialExtents)
5182 p = & childExtents;
5183 else
5184 p = NULL;
5185
a70eb13e
JS
5186 int maxDescent = 0;
5187 int maxAscent = 0;
5188 int maxLineHeight = 0;
5189
5d7836c4
JS
5190 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5191 while (node)
5192 {
5193 wxRichTextObject* child = node->GetData();
5194 if (!child->GetRange().IsOutside(range))
5195 {
cdaed652
VZ
5196 // Floating objects have a zero size within the paragraph.
5197 if (child->IsFloating())
5198 {
5199 if (partialExtents)
5200 {
5201 int lastSize;
5202 if (partialExtents->GetCount() > 0)
5203 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5204 else
5205 lastSize = 0;
5206
5207 partialExtents->Add(0 /* zero size */ + lastSize);
5208 }
5209 }
5210 else
5211 {
603f702b 5212 wxSize childSize;
4f3d5bc0 5213
603f702b
JS
5214 wxRichTextRange rangeToUse = range;
5215 rangeToUse.LimitTo(child->GetRange());
603f702b 5216 int childDescent = 0;
31778480 5217
7c9fdebe 5218 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
603f702b
JS
5219 // but it's only going to be used after caching has taken place.
5220 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5221 {
603f702b
JS
5222 childDescent = child->GetDescent();
5223 childSize = child->GetCachedSize();
2f45f554 5224
a70eb13e
JS
5225 if (childDescent == 0)
5226 {
5227 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5228 }
5229 else
5230 {
5231 maxDescent = wxMax(maxDescent, childDescent);
5232 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5233 }
5234
5235 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5236
5237 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5238 sz.x += childSize.x;
a70eb13e 5239 descent = maxDescent;
603f702b
JS
5240 }
5241 else if (child->IsTopLevel())
31778480 5242 {
603f702b
JS
5243 childDescent = child->GetDescent();
5244 childSize = child->GetCachedSize();
31778480 5245
a70eb13e
JS
5246 if (childDescent == 0)
5247 {
5248 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5249 }
5250 else
5251 {
5252 maxDescent = wxMax(maxDescent, childDescent);
5253 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5254 }
5255
5256 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5257
5258 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5259 sz.x += childSize.x;
a70eb13e
JS
5260 descent = maxDescent;
5261
5262 // FIXME: this won't change the original values.
5263 // Should we be calling GetRangeSize above instead of using cached values?
5264#if 0
603f702b 5265 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5266 {
603f702b
JS
5267 child->SetCachedSize(childSize);
5268 child->SetDescent(childDescent);
31778480 5269 }
a70eb13e 5270#endif
31778480 5271
603f702b
JS
5272 if (partialExtents)
5273 {
5274 int lastSize;
5275 if (partialExtents->GetCount() > 0)
5276 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5277 else
5278 lastSize = 0;
5279
5280 partialExtents->Add(childSize.x + lastSize);
5281 }
5282 }
8db2e3ef 5283 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b 5284 {
a70eb13e
JS
5285 if (childDescent == 0)
5286 {
5287 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5288 }
5289 else
5290 {
5291 maxDescent = wxMax(maxDescent, childDescent);
5292 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5293 }
5294
5295 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5296
5297 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5298 sz.x += childSize.x;
a70eb13e 5299 descent = maxDescent;
603f702b
JS
5300
5301 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5302 {
5303 child->SetCachedSize(childSize);
5304 child->SetDescent(childDescent);
5305 }
5306
5307 if (partialExtents)
5308 {
5309 int lastSize;
5310 if (partialExtents->GetCount() > 0)
5311 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5312 else
5313 lastSize = 0;
5314
5315 size_t i;
5316 for (i = 0; i < childExtents.GetCount(); i++)
5317 {
5318 partialExtents->Add(childExtents[i] + lastSize);
5319 }
5320 }
5321 }
5322 }
5323
5324 if (p)
5325 p->Clear();
5d7836c4
JS
5326 }
5327
5328 node = node->GetNext();
5329 }
5330 size = sz;
5331 }
5332 else
5333 {
5334 // Use formatted data, with line breaks
5335 wxSize sz;
5336
5337 // We're going to loop through each line, and then for each line,
5338 // call GetRangeSize for the fragment that comprises that line.
5339 // Only we have to do that multiple times within the line, because
5340 // the line may be broken into pieces. For now ignore line break commands
5341 // (so we can assume that getting the unformatted size for a fragment
5342 // within a line is the actual size)
5343
5344 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5345 while (node)
5346 {
5347 wxRichTextLine* line = node->GetData();
1e967276
JS
5348 wxRichTextRange lineRange = line->GetAbsoluteRange();
5349 if (!lineRange.IsOutside(range))
5d7836c4 5350 {
a70eb13e
JS
5351 int maxDescent = 0;
5352 int maxAscent = 0;
5353 int maxLineHeight = 0;
5354 int maxLineWidth = 0;
7fe8059f 5355
5d7836c4
JS
5356 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5357 while (node2)
5358 {
5359 wxRichTextObject* child = node2->GetData();
7fe8059f 5360
cdaed652 5361 if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
5d7836c4 5362 {
1e967276 5363 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5364 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5365 if (child->IsTopLevel())
5366 rangeToUse = child->GetOwnRange();
7fe8059f 5367
5d7836c4
JS
5368 wxSize childSize;
5369 int childDescent = 0;
8db2e3ef 5370 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
5d7836c4 5371 {
a70eb13e
JS
5372 if (childDescent == 0)
5373 {
5374 // Assume that if descent is zero, this child can occupy the full line height
5375 // and does not need space for the line's maximum descent. So we influence
5376 // the overall max line height only.
5377 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5378 }
5379 else
5380 {
5381 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5382 maxDescent = wxMax(maxAscent, childDescent);
5383 }
5384 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5385 maxLineWidth += childSize.x;
5d7836c4 5386 }
5d7836c4 5387 }
7fe8059f 5388
5d7836c4
JS
5389 node2 = node2->GetNext();
5390 }
5391
a70eb13e
JS
5392 descent = wxMax(descent, maxDescent);
5393
5d7836c4 5394 // Increase size by a line (TODO: paragraph spacing)
a70eb13e
JS
5395 sz.y += maxLineHeight;
5396 sz.x = wxMax(sz.x, maxLineWidth);
5d7836c4
JS
5397 }
5398 node = node->GetNext();
5399 }
5400 size = sz;
5401 }
5402 return true;
5403}
5404
5405/// Finds the absolute position and row height for the given character position
8db2e3ef 5406bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5407{
5408 if (index == -1)
5409 {
5410 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5411 if (line)
5412 *height = line->GetSize().y;
5413 else
5414 *height = dc.GetCharHeight();
5415
5416 // -1 means 'the start of the buffer'.
5417 pt = GetPosition();
5418 if (line)
5419 pt = pt + line->GetPosition();
5420
5d7836c4
JS
5421 return true;
5422 }
5423
5424 // The final position in a paragraph is taken to mean the position
5425 // at the start of the next paragraph.
5426 if (index == GetRange().GetEnd())
5427 {
5428 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5429 wxASSERT( parent != NULL );
5430
5431 // Find the height at the next paragraph, if any
5432 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5433 if (line)
5434 {
5435 *height = line->GetSize().y;
5436 pt = line->GetAbsolutePosition();
5437 }
5438 else
5439 {
5440 *height = dc.GetCharHeight();
5441 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5442 pt = wxPoint(indent, GetCachedSize().y);
5443 }
5444
5445 return true;
5446 }
5447
5448 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5449 return false;
5450
5451 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5452 while (node)
5453 {
5454 wxRichTextLine* line = node->GetData();
1e967276
JS
5455 wxRichTextRange lineRange = line->GetAbsoluteRange();
5456 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5457 {
5458 // If this is the last point in the line, and we're forcing the
5459 // returned value to be the start of the next line, do the required
5460 // thing.
1e967276 5461 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5462 {
5463 if (node->GetNext())
5464 {
5465 wxRichTextLine* nextLine = node->GetNext()->GetData();
5466 *height = nextLine->GetSize().y;
5467 pt = nextLine->GetAbsolutePosition();
5468 return true;
5469 }
5470 }
5471
5472 pt.y = line->GetPosition().y + GetPosition().y;
5473
1e967276 5474 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5475 wxSize rangeSize;
5476 int descent = 0;
5477
5478 // We find the size of the line up to this point,
5479 // then we can add this size to the line start position and
5480 // paragraph start position to find the actual position.
5481
8db2e3ef 5482 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5483 {
5484 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5485 *height = line->GetSize().y;
5486
5487 return true;
5488 }
5489
5490 }
5491
5492 node = node->GetNext();
5493 }
5494
5495 return false;
5496}
5497
5498/// Hit-testing: returns a flag indicating hit test details, plus
5499/// information about position
8db2e3ef 5500int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5501{
603f702b
JS
5502 if (!IsShown())
5503 return wxRICHTEXT_HITTEST_NONE;
5504
5505 // If we're in the top-level container, then we can return
5506 // a suitable hit test code even if the point is outside the container area,
5507 // so that we can position the caret sensibly even if we don't
5508 // click on valid content. If we're not at the top-level, and the point
5509 // is not within this paragraph object, then we don't want to stop more
5510 // precise hit-testing from working prematurely, so return immediately.
5511 // NEW STRATEGY: use the parent boundary to test whether we're in the
5512 // right region, not the paragraph, since the paragraph may be positioned
5513 // some way in from where the user clicks.
5514 {
5515 long tmpPos;
5516 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5517 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5518 return wxRICHTEXT_HITTEST_NONE;
5519 }
5520
5521 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5522 while (objNode)
5523 {
5524 wxRichTextObject* child = objNode->GetData();
7c9fdebe
JS
5525 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5526 // and also, if this seems composite but actually is marked as atomic,
5527 // don't recurse.
5528 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5529 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
603f702b
JS
5530 {
5531 {
8db2e3ef 5532 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5533 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5534 return hitTest;
5535 }
5536 }
5537
5538 objNode = objNode->GetNext();
5539 }
5540
5d7836c4
JS
5541 wxPoint paraPos = GetPosition();
5542
5543 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5544 while (node)
5545 {
5546 wxRichTextLine* line = node->GetData();
5547 wxPoint linePos = paraPos + line->GetPosition();
5548 wxSize lineSize = line->GetSize();
1e967276 5549 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5550
62381daa 5551 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5552 {
5553 if (pt.x < linePos.x)
5554 {
1e967276 5555 textPosition = lineRange.GetStart();
603f702b
JS
5556 *obj = FindObjectAtPosition(textPosition);
5557 *contextObj = GetContainer();
f262b25c 5558 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5559 }
5560 else if (pt.x >= (linePos.x + lineSize.x))
5561 {
1e967276 5562 textPosition = lineRange.GetEnd();
603f702b
JS
5563 *obj = FindObjectAtPosition(textPosition);
5564 *contextObj = GetContainer();
f262b25c 5565 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5566 }
5567 else
5568 {
2f45f554
JS
5569#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5570 wxArrayInt partialExtents;
5571
5572 wxSize paraSize;
5573 int paraDescent;
5574
5575 // This calculates the partial text extents
8db2e3ef 5576 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
2f45f554
JS
5577
5578 int lastX = linePos.x;
5579 size_t i;
5580 for (i = 0; i < partialExtents.GetCount(); i++)
5581 {
5582 int nextX = partialExtents[i] + linePos.x;
5583
5584 if (pt.x >= lastX && pt.x <= nextX)
5585 {
5586 textPosition = i + lineRange.GetStart(); // minus 1?
5587
603f702b
JS
5588 *obj = FindObjectAtPosition(textPosition);
5589 *contextObj = GetContainer();
5590
2f45f554
JS
5591 // So now we know it's between i-1 and i.
5592 // Let's see if we can be more precise about
5593 // which side of the position it's on.
5594
cdaed652 5595 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5596 if (pt.x >= midPoint)
5597 return wxRICHTEXT_HITTEST_AFTER;
5598 else
5599 return wxRICHTEXT_HITTEST_BEFORE;
5600 }
5601
5602 lastX = nextX;
5603 }
5604#else
5d7836c4
JS
5605 long i;
5606 int lastX = linePos.x;
1e967276 5607 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5608 {
5609 wxSize childSize;
5610 int descent = 0;
7fe8059f 5611
1e967276 5612 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5613
8db2e3ef 5614 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5615
5616 int nextX = childSize.x + linePos.x;
5617
5618 if (pt.x >= lastX && pt.x <= nextX)
5619 {
5620 textPosition = i;
5621
603f702b
JS
5622 *obj = FindObjectAtPosition(textPosition);
5623 *contextObj = GetContainer();
5624
5d7836c4
JS
5625 // So now we know it's between i-1 and i.
5626 // Let's see if we can be more precise about
5627 // which side of the position it's on.
5628
cdaed652 5629 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5630 if (pt.x >= midPoint)
5631 return wxRICHTEXT_HITTEST_AFTER;
5632 else
5633 return wxRICHTEXT_HITTEST_BEFORE;
5634 }
5635 else
5636 {
5637 lastX = nextX;
5638 }
5639 }
2f45f554 5640#endif
5d7836c4
JS
5641 }
5642 }
7fe8059f 5643
5d7836c4
JS
5644 node = node->GetNext();
5645 }
5646
5647 return wxRICHTEXT_HITTEST_NONE;
5648}
5649
5650/// Split an object at this position if necessary, and return
5651/// the previous object, or NULL if inserting at beginning.
5652wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5653{
5654 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5655 while (node)
5656 {
5657 wxRichTextObject* child = node->GetData();
5658
5659 if (pos == child->GetRange().GetStart())
5660 {
5661 if (previousObject)
4d551ad5
JS
5662 {
5663 if (node->GetPrevious())
5664 *previousObject = node->GetPrevious()->GetData();
5665 else
5666 *previousObject = NULL;
5667 }
5d7836c4
JS
5668
5669 return child;
5670 }
5671
5672 if (child->GetRange().Contains(pos))
5673 {
5674 // This should create a new object, transferring part of
5675 // the content to the old object and the rest to the new object.
5676 wxRichTextObject* newObject = child->DoSplit(pos);
5677
5678 // If we couldn't split this object, just insert in front of it.
5679 if (!newObject)
5680 {
5681 // Maybe this is an empty string, try the next one
5682 // return child;
5683 }
5684 else
5685 {
5686 // Insert the new object after 'child'
5687 if (node->GetNext())
5688 m_children.Insert(node->GetNext(), newObject);
5689 else
5690 m_children.Append(newObject);
5691 newObject->SetParent(this);
5692
5693 if (previousObject)
5694 *previousObject = child;
5695
5696 return newObject;
5697 }
5698 }
5699
5700 node = node->GetNext();
5701 }
5702 if (previousObject)
5703 *previousObject = NULL;
5704 return NULL;
5705}
5706
5707/// Move content to a list from obj on
5708void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5709{
5710 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5711 while (node)
5712 {
5713 wxRichTextObject* child = node->GetData();
5714 list.Append(child);
5715
5716 wxRichTextObjectList::compatibility_iterator oldNode = node;
5717
5718 node = node->GetNext();
5719
5720 m_children.DeleteNode(oldNode);
5721 }
5722}
5723
5724/// Add content back from list
5725void wxRichTextParagraph::MoveFromList(wxList& list)
5726{
09f14108 5727 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5728 {
5729 AppendChild((wxRichTextObject*) node->GetData());
5730 }
5731}
5732
5733/// Calculate range
5734void wxRichTextParagraph::CalculateRange(long start, long& end)
5735{
5736 wxRichTextCompositeObject::CalculateRange(start, end);
5737
5738 // Add one for end of paragraph
5739 end ++;
5740
5741 m_range.SetRange(start, end);
5742}
5743
5744/// Find the object at the given position
5745wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5746{
5747 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5748 while (node)
5749 {
5750 wxRichTextObject* obj = node->GetData();
603f702b
JS
5751 if (obj->GetRange().Contains(position) ||
5752 obj->GetRange().GetStart() == position ||
5753 obj->GetRange().GetEnd() == position)
5d7836c4 5754 return obj;
7fe8059f 5755
5d7836c4
JS
5756 node = node->GetNext();
5757 }
5758 return NULL;
5759}
5760
5761/// Get the plain text searching from the start or end of the range.
5762/// The resulting string may be shorter than the range given.
5763bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5764{
5765 text = wxEmptyString;
5766
5767 if (fromStart)
5768 {
5769 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5770 while (node)
5771 {
5772 wxRichTextObject* obj = node->GetData();
5773 if (!obj->GetRange().IsOutside(range))
5774 {
5775 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5776 if (textObj)
5777 {
5778 text += textObj->GetTextForRange(range);
5779 }
5780 else
043c0d58
JS
5781 {
5782 text += wxT(" ");
5783 }
5d7836c4
JS
5784 }
5785
5786 node = node->GetNext();
5787 }
5788 }
5789 else
5790 {
5791 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5792 while (node)
5793 {
5794 wxRichTextObject* obj = node->GetData();
5795 if (!obj->GetRange().IsOutside(range))
5796 {
5797 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5798 if (textObj)
5799 {
5800 text = textObj->GetTextForRange(range) + text;
5801 }
5802 else
043c0d58
JS
5803 {
5804 text = wxT(" ") + text;
5805 }
5d7836c4
JS
5806 }
5807
5808 node = node->GetPrevious();
5809 }
5810 }
5811
5812 return true;
5813}
5814
5815/// Find a suitable wrap position.
8db2e3ef 5816bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5817{
72945e24
JS
5818 if (range.GetLength() <= 0)
5819 return false;
5820
5d7836c4
JS
5821 // Find the first position where the line exceeds the available space.
5822 wxSize sz;
5d7836c4 5823 long breakPosition = range.GetEnd();
ecb5fbf1 5824
31778480
JS
5825#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5826 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 5827 {
31778480 5828 int widthBefore;
5d7836c4 5829
31778480
JS
5830 if (range.GetStart() > GetRange().GetStart())
5831 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5832 else
5833 widthBefore = 0;
5834
5835 size_t i;
43a0d1e1 5836 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 5837 {
31778480 5838 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 5839
72945e24 5840 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 5841 {
31778480
JS
5842 breakPosition = i-1;
5843 break;
ecb5fbf1 5844 }
5d7836c4 5845 }
31778480
JS
5846 }
5847 else
5848#endif
5849 {
5850 // Binary chop for speed
5851 long minPos = range.GetStart();
5852 long maxPos = range.GetEnd();
5853 while (true)
ecb5fbf1 5854 {
31778480
JS
5855 if (minPos == maxPos)
5856 {
5857 int descent = 0;
8db2e3ef 5858 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 5859
31778480
JS
5860 if (sz.x > availableSpace)
5861 breakPosition = minPos - 1;
5862 break;
5863 }
5864 else if ((maxPos - minPos) == 1)
ecb5fbf1 5865 {
31778480 5866 int descent = 0;
8db2e3ef 5867 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5868
5869 if (sz.x > availableSpace)
5870 breakPosition = minPos - 1;
5871 else
5872 {
8db2e3ef 5873 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5874 if (sz.x > availableSpace)
5875 breakPosition = maxPos-1;
5876 }
5877 break;
ecb5fbf1
JS
5878 }
5879 else
5880 {
31778480
JS
5881 long nextPos = minPos + ((maxPos - minPos) / 2);
5882
5883 int descent = 0;
8db2e3ef 5884 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5885
5886 if (sz.x > availableSpace)
5887 {
5888 maxPos = nextPos;
5889 }
5890 else
5891 {
5892 minPos = nextPos;
5893 }
ecb5fbf1
JS
5894 }
5895 }
5d7836c4
JS
5896 }
5897
5898 // Now we know the last position on the line.
5899 // Let's try to find a word break.
5900
5901 wxString plainText;
5902 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
5903 {
ff76711f
JS
5904 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
5905 if (newLinePos != wxNOT_FOUND)
5d7836c4 5906 {
ff76711f
JS
5907 breakPosition = wxMax(0, range.GetStart() + newLinePos);
5908 }
5909 else
5910 {
5911 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
5912 int tabPos = plainText.Find(wxT('\t'), true);
5913 int pos = wxMax(spacePos, tabPos);
5914 if (pos != wxNOT_FOUND)
ff76711f 5915 {
31002e44 5916 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
5917 breakPosition = breakPosition - positionsFromEndOfString;
5918 }
5d7836c4
JS
5919 }
5920 }
5921
5922 wrapPosition = breakPosition;
5923
5924 return true;
5925}
5926
5927/// Get the bullet text for this paragraph.
5928wxString wxRichTextParagraph::GetBulletText()
5929{
5930 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
5931 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
5932 return wxEmptyString;
5933
5934 int number = GetAttributes().GetBulletNumber();
5935
5936 wxString text;
d2d0adc7 5937 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
5938 {
5939 text.Printf(wxT("%d"), number);
5940 }
5941 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
5942 {
5943 // TODO: Unicode, and also check if number > 26
5944 text.Printf(wxT("%c"), (wxChar) (number+64));
5945 }
5946 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
5947 {
5948 // TODO: Unicode, and also check if number > 26
5949 text.Printf(wxT("%c"), (wxChar) (number+96));
5950 }
5951 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
5952 {
59509217 5953 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
5954 }
5955 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
5956 {
59509217
JS
5957 text = wxRichTextDecimalToRoman(number);
5958 text.MakeLower();
5d7836c4
JS
5959 }
5960 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
5961 {
d2d0adc7
JS
5962 text = GetAttributes().GetBulletText();
5963 }
3e541562 5964
d2d0adc7
JS
5965 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
5966 {
5967 // The outline style relies on the text being computed statically,
5968 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5969 // should be stored in the attributes; if not, just use the number for this
5970 // level, as previously computed.
5971 if (!GetAttributes().GetBulletText().IsEmpty())
5972 text = GetAttributes().GetBulletText();
5d7836c4
JS
5973 }
5974
5975 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
5976 {
5977 text = wxT("(") + text + wxT(")");
5978 }
d2d0adc7
JS
5979 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
5980 {
5981 text = text + wxT(")");
5982 }
5983
5d7836c4
JS
5984 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
5985 {
5986 text += wxT(".");
5987 }
5988
5989 return text;
5990}
5991
1e967276
JS
5992/// Allocate or reuse a line object
5993wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
5994{
5995 if (pos < (int) m_cachedLines.GetCount())
5996 {
5997 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
5998 line->Init(this);
5999 return line;
6000 }
6001 else
6002 {
6003 wxRichTextLine* line = new wxRichTextLine(this);
6004 m_cachedLines.Append(line);
6005 return line;
6006 }
6007}
6008
6009/// Clear remaining unused line objects, if any
6010bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6011{
6012 int cachedLineCount = m_cachedLines.GetCount();
6013 if ((int) cachedLineCount > lineCount)
6014 {
6015 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6016 {
6017 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6018 wxRichTextLine* line = node->GetData();
6019 m_cachedLines.Erase(node);
6020 delete line;
6021 }
6022 }
6023 return true;
6024}
6025
fe5aa22c
JS
6026/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6027/// retrieve the actual style.
603f702b 6028wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 6029{
24777478 6030 wxRichTextAttr attr;
603f702b 6031 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6032 if (buf)
6033 {
6034 attr = buf->GetBasicStyle();
603f702b
JS
6035 if (!includingBoxAttr)
6036 {
6037 attr.GetTextBoxAttr().Reset();
6038 // The background colour will be painted by the container, and we don't
6039 // want to unnecessarily overwrite the background when we're drawing text
6040 // because this may erase the guideline (which appears just under the text
6041 // if there's no padding).
6042 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6043 }
fe5aa22c
JS
6044 wxRichTextApplyStyle(attr, GetAttributes());
6045 }
6046 else
6047 attr = GetAttributes();
6048
6049 wxRichTextApplyStyle(attr, contentStyle);
6050 return attr;
6051}
6052
6053/// Get combined attributes of the base style and paragraph style.
603f702b 6054wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 6055{
24777478 6056 wxRichTextAttr attr;
603f702b 6057 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6058 if (buf)
6059 {
6060 attr = buf->GetBasicStyle();
603f702b
JS
6061 if (!includingBoxAttr)
6062 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
6063 wxRichTextApplyStyle(attr, GetAttributes());
6064 }
6065 else
6066 attr = GetAttributes();
6067
6068 return attr;
6069}
5d7836c4 6070
603f702b 6071// Create default tabstop array
cfa3b256
JS
6072void wxRichTextParagraph::InitDefaultTabs()
6073{
6074 // create a default tab list at 10 mm each.
6075 for (int i = 0; i < 20; ++i)
6076 {
6077 sm_defaultTabs.Add(i*100);
6078 }
6079}
6080
603f702b 6081// Clear default tabstop array
cfa3b256
JS
6082void wxRichTextParagraph::ClearDefaultTabs()
6083{
6084 sm_defaultTabs.Clear();
6085}
6086
8db2e3ef 6087void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
6088{
6089 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6090 while (node)
6091 {
bec80f4f 6092 wxRichTextObject* anchored = node->GetData();
07d4142f 6093 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652
VZ
6094 {
6095 wxSize size;
6096 int descent, x = 0;
8db2e3ef 6097 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
bec80f4f 6098
24777478 6099 int offsetY = 0;
603f702b 6100 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
6101 {
6102 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6103 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6104 {
6105 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6106 }
6107 }
bec80f4f 6108
24777478 6109 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6110
cdaed652 6111 /* Update the offset */
24777478
JS
6112 int newOffsetY = pos - rect.y;
6113 if (newOffsetY != offsetY)
6114 {
6115 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6116 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6117 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6118 }
cdaed652 6119
24777478 6120 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6121 x = rect.x;
24777478 6122 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6123 x = rect.x + rect.width - size.x;
24777478 6124
cdaed652
VZ
6125 anchored->SetPosition(wxPoint(x, pos));
6126 anchored->SetCachedSize(size);
6127 floatCollector->CollectFloat(this, anchored);
6128 }
6129
6130 node = node->GetNext();
6131 }
6132}
6133
603f702b 6134// Get the first position from pos that has a line break character.
ff76711f
JS
6135long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6136{
6137 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6138 while (node)
6139 {
6140 wxRichTextObject* obj = node->GetData();
6141 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6142 {
6143 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6144 if (textObj)
6145 {
6146 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6147 if (breakPos > -1)
6148 return breakPos;
6149 }
6150 }
6151 node = node->GetNext();
6152 }
6153 return -1;
6154}
cfa3b256 6155
5d7836c4
JS
6156/*!
6157 * wxRichTextLine
6158 * This object represents a line in a paragraph, and stores
6159 * offsets from the start of the paragraph representing the
6160 * start and end positions of the line.
6161 */
6162
6163wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6164{
1e967276 6165 Init(parent);
5d7836c4
JS
6166}
6167
6168/// Initialisation
1e967276 6169void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6170{
1e967276
JS
6171 m_parent = parent;
6172 m_range.SetRange(-1, -1);
6173 m_pos = wxPoint(0, 0);
6174 m_size = wxSize(0, 0);
5d7836c4 6175 m_descent = 0;
2f45f554
JS
6176#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6177 m_objectSizes.Clear();
6178#endif
5d7836c4
JS
6179}
6180
6181/// Copy
6182void wxRichTextLine::Copy(const wxRichTextLine& obj)
6183{
6184 m_range = obj.m_range;
2f45f554
JS
6185#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6186 m_objectSizes = obj.m_objectSizes;
6187#endif
5d7836c4
JS
6188}
6189
6190/// Get the absolute object position
6191wxPoint wxRichTextLine::GetAbsolutePosition() const
6192{
6193 return m_parent->GetPosition() + m_pos;
6194}
6195
1e967276
JS
6196/// Get the absolute range
6197wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6198{
6199 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6200 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6201 return range;
6202}
6203
5d7836c4
JS
6204/*!
6205 * wxRichTextPlainText
6206 * This object represents a single piece of text.
6207 */
6208
6209IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6210
24777478 6211wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6212 wxRichTextObject(parent)
6213{
5d7836c4
JS
6214 if (style)
6215 SetAttributes(*style);
6216
6217 m_text = text;
6218}
6219
cfa3b256
JS
6220#define USE_KERNING_FIX 1
6221
4794d69c
JS
6222// If insufficient tabs are defined, this is the tab width used
6223#define WIDTH_FOR_DEFAULT_TABS 50
6224
5d7836c4 6225/// Draw the item
8db2e3ef 6226bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6227{
fe5aa22c
JS
6228 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6229 wxASSERT (para != NULL);
6230
603f702b 6231 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6232 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6233
6234 // Let's make the assumption for now that for content in a paragraph, including
6235 // text, we never have a discontinuous selection. So we only deal with a
6236 // single range.
6237 wxRichTextRange selectionRange;
6238 if (selection.IsValid())
6239 {
6240 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6241 if (selectionRanges.GetCount() > 0)
6242 selectionRange = selectionRanges[0];
6243 else
6244 selectionRange = wxRICHTEXT_NO_SELECTION;
6245 }
6246 else
6247 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6248
5d7836c4
JS
6249 int offset = GetRange().GetStart();
6250
ff76711f
JS
6251 // Replace line break characters with spaces
6252 wxString str = m_text;
6253 wxString toRemove = wxRichTextLineBreakChar;
6254 str.Replace(toRemove, wxT(" "));
c025e094
JS
6255 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6256 str.MakeUpper();
3e541562 6257
5d7836c4 6258 long len = range.GetLength();
ff76711f 6259 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6260
5d7836c4
JS
6261 // Test for the optimized situations where all is selected, or none
6262 // is selected.
6263
30bf7630
JS
6264 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6265 wxCheckSetFont(dc, textFont);
6266 int charHeight = dc.GetCharHeight();
6267
6268 int x, y;
a1b806b9 6269 if ( textFont.IsOk() )
30bf7630
JS
6270 {
6271 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6272 {
32423dd8
JS
6273 if (textFont.IsUsingSizeInPixels())
6274 {
6275 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
4ba36292 6276 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
32423dd8
JS
6277 x = rect.x;
6278 y = rect.y;
6279 }
6280 else
6281 {
6282 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6283 textFont.SetPointSize(static_cast<int>(size));
32423dd8
JS
6284 x = rect.x;
6285 y = rect.y;
6286 }
30bf7630
JS
6287 wxCheckSetFont(dc, textFont);
6288 }
6289 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6290 {
32423dd8
JS
6291 if (textFont.IsUsingSizeInPixels())
6292 {
6293 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6294 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6295 x = rect.x;
4ba36292 6296 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6297 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6298 }
6299 else
6300 {
6301 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6302 textFont.SetPointSize(static_cast<int>(size));
32423dd8 6303 x = rect.x;
4ba36292 6304 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6305 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6306 }
30bf7630
JS
6307 wxCheckSetFont(dc, textFont);
6308 }
6309 else
6310 {
6311 x = rect.x;
6312 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6313 }
6314 }
6315 else
6316 {
6317 x = rect.x;
6318 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6319 }
5d7836c4 6320
603f702b
JS
6321 // TODO: new selection code
6322
5d7836c4
JS
6323 // (a) All selected.
6324 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6325 {
fe5aa22c 6326 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6327 }
6328 // (b) None selected.
6329 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6330 {
6331 // Draw all unselected
fe5aa22c 6332 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6333 }
6334 else
6335 {
6336 // (c) Part selected, part not
6337 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6338
04ee05f9 6339 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6340
5d7836c4
JS
6341 // 1. Initial unselected chunk, if any, up until start of selection.
6342 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6343 {
6344 int r1 = range.GetStart();
6345 int s1 = selectionRange.GetStart()-1;
6346 int fragmentLen = s1 - r1 + 1;
6347 if (fragmentLen < 0)
af588446 6348 {
5d7836c4 6349 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6350 }
ff76711f 6351 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6352
fe5aa22c 6353 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6354
6355#if USE_KERNING_FIX
6356 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6357 {
6358 // Compensate for kerning difference
ff76711f
JS
6359 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6360 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6361
cfa3b256
JS
6362 wxCoord w1, h1, w2, h2, w3, h3;
6363 dc.GetTextExtent(stringFragment, & w1, & h1);
6364 dc.GetTextExtent(stringFragment2, & w2, & h2);
6365 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6366
cfa3b256
JS
6367 int kerningDiff = (w1 + w3) - w2;
6368 x = x - kerningDiff;
6369 }
6370#endif
5d7836c4
JS
6371 }
6372
6373 // 2. Selected chunk, if any.
6374 if (selectionRange.GetEnd() >= range.GetStart())
6375 {
6376 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6377 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6378
6379 int fragmentLen = s2 - s1 + 1;
6380 if (fragmentLen < 0)
af588446 6381 {
5d7836c4 6382 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6383 }
ff76711f 6384 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6385
fe5aa22c 6386 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6387
6388#if USE_KERNING_FIX
6389 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6390 {
6391 // Compensate for kerning difference
ff76711f
JS
6392 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6393 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6394
cfa3b256
JS
6395 wxCoord w1, h1, w2, h2, w3, h3;
6396 dc.GetTextExtent(stringFragment, & w1, & h1);
6397 dc.GetTextExtent(stringFragment2, & w2, & h2);
6398 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6399
cfa3b256
JS
6400 int kerningDiff = (w1 + w3) - w2;
6401 x = x - kerningDiff;
6402 }
6403#endif
5d7836c4
JS
6404 }
6405
6406 // 3. Remaining unselected chunk, if any
6407 if (selectionRange.GetEnd() < range.GetEnd())
6408 {
6409 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6410 int r2 = range.GetEnd();
6411
6412 int fragmentLen = r2 - s2 + 1;
6413 if (fragmentLen < 0)
af588446 6414 {
5d7836c4 6415 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6416 }
ff76711f 6417 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6418
fe5aa22c 6419 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6420 }
5d7836c4
JS
6421 }
6422
6423 return true;
6424}
61399247 6425
24777478 6426bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6427{
cfa3b256
JS
6428 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6429
6430 wxArrayInt tabArray;
6431 int tabCount;
6432 if (hasTabs)
ab14c7aa 6433 {
cfa3b256
JS
6434 if (attr.GetTabs().IsEmpty())
6435 tabArray = wxRichTextParagraph::GetDefaultTabs();
6436 else
6437 tabArray = attr.GetTabs();
6438 tabCount = tabArray.GetCount();
6439
6440 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6441 {
cfa3b256
JS
6442 int pos = tabArray[i];
6443 pos = ConvertTenthsMMToPixels(dc, pos);
6444 tabArray[i] = pos;
7f0d9d71
JS
6445 }
6446 }
cfa3b256
JS
6447 else
6448 tabCount = 0;
ab14c7aa 6449
cfa3b256
JS
6450 int nextTabPos = -1;
6451 int tabPos = -1;
7f0d9d71 6452 wxCoord w, h;
ab14c7aa 6453
cfa3b256 6454 if (selected)
ab14c7aa 6455 {
0ec6da02
JS
6456 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6457 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6458
ecb5fbf1
JS
6459 wxCheckSetBrush(dc, wxBrush(highlightColour));
6460 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6461 dc.SetTextForeground(highlightTextColour);
04ee05f9 6462 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6463 }
ab14c7aa
JS
6464 else
6465 {
fe5aa22c 6466 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6467
f0e9eda2
JS
6468 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6469 {
04ee05f9 6470 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6471 dc.SetTextBackground(attr.GetBackgroundColour());
6472 }
6473 else
04ee05f9 6474 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6475 }
3e541562 6476
925a662a 6477 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6478 while (hasTabs)
ab14c7aa
JS
6479 {
6480 // the string has a tab
7f0d9d71
JS
6481 // break up the string at the Tab
6482 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6483 str = str.AfterFirst(wxT('\t'));
6484 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6485 tabPos = x + w;
7f0d9d71 6486 bool not_found = true;
cfa3b256 6487 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6488 {
015d0446 6489 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6490
6491 // Find the next tab position.
6492 // Even if we're at the end of the tab array, we must still draw the chunk.
6493
6494 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6495 {
4794d69c
JS
6496 if (nextTabPos <= tabPos)
6497 {
6498 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6499 nextTabPos = tabPos + defaultTabWidth;
6500 }
6501
7f0d9d71 6502 not_found = false;
ab14c7aa
JS
6503 if (selected)
6504 {
cfa3b256 6505 w = nextTabPos - x;
7f0d9d71 6506 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6507 dc.DrawRectangle(selRect);
7f0d9d71
JS
6508 }
6509 dc.DrawText(stringChunk, x, y);
42688aea
JS
6510
6511 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6512 {
6513 wxPen oldPen = dc.GetPen();
ecb5fbf1 6514 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6515 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6516 wxCheckSetPen(dc, oldPen);
42688aea
JS
6517 }
6518
cfa3b256 6519 x = nextTabPos;
7f0d9d71
JS
6520 }
6521 }
cfa3b256 6522 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6523 }
61399247 6524
cfa3b256 6525 if (!str.IsEmpty())
ab14c7aa 6526 {
cfa3b256
JS
6527 dc.GetTextExtent(str, & w, & h);
6528 if (selected)
6529 {
6530 wxRect selRect(x, rect.y, w, rect.GetHeight());
6531 dc.DrawRectangle(selRect);
6532 }
6533 dc.DrawText(str, x, y);
42688aea
JS
6534
6535 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6536 {
6537 wxPen oldPen = dc.GetPen();
ecb5fbf1 6538 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6539 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6540 wxCheckSetPen(dc, oldPen);
42688aea
JS
6541 }
6542
cfa3b256 6543 x += w;
7f0d9d71 6544 }
5d7836c4 6545
7c9fdebe 6546 return true;
7f0d9d71 6547}
fe5aa22c 6548
5d7836c4 6549/// Lay the item out
8db2e3ef 6550bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6551{
ecb5fbf1
JS
6552 // Only lay out if we haven't already cached the size
6553 if (m_size.x == -1)
8db2e3ef 6554 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6555 m_maxSize = m_size;
6556 // Eventually we want to have a reasonable estimate of minimum size.
6557 m_minSize = wxSize(0, 0);
5d7836c4
JS
6558 return true;
6559}
6560
6561/// Copy
6562void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6563{
6564 wxRichTextObject::Copy(obj);
6565
6566 m_text = obj.m_text;
6567}
6568
6569/// Get/set the object size for the given range. Returns false if the range
6570/// is invalid for this object.
8db2e3ef 6571bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
6572{
6573 if (!range.IsWithin(GetRange()))
6574 return false;
6575
fe5aa22c
JS
6576 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6577 wxASSERT (para != NULL);
603f702b 6578
925a662a 6579 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6580
24777478 6581 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6582 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6583
5d7836c4
JS
6584 // Always assume unformatted text, since at this level we have no knowledge
6585 // of line breaks - and we don't need it, since we'll calculate size within
6586 // formatted text by doing it in chunks according to the line ranges
6587
30bf7630 6588 bool bScript(false);
44cc96a8 6589 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6590 if (font.IsOk())
30bf7630
JS
6591 {
6592 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6593 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6594 {
6595 wxFont textFont = font;
32423dd8
JS
6596 if (textFont.IsUsingSizeInPixels())
6597 {
6598 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6599 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6600 }
6601 else
6602 {
6603 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6604 textFont.SetPointSize(static_cast<int>(size));
6605 }
30bf7630
JS
6606 wxCheckSetFont(dc, textFont);
6607 bScript = true;
6608 }
6609 else
6610 {
6611 wxCheckSetFont(dc, font);
6612 }
6613 }
5d7836c4 6614
109bfc88 6615 bool haveDescent = false;
5d7836c4
JS
6616 int startPos = range.GetStart() - GetRange().GetStart();
6617 long len = range.GetLength();
3e541562 6618
ff76711f
JS
6619 wxString str(m_text);
6620 wxString toReplace = wxRichTextLineBreakChar;
6621 str.Replace(toReplace, wxT(" "));
6622
6623 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea
JS
6624
6625 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6626 stringChunk.MakeUpper();
6627
5d7836c4 6628 wxCoord w, h;
7f0d9d71 6629 int width = 0;
cfa3b256 6630 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6631 {
6632 // the string has a tab
cfa3b256
JS
6633 wxArrayInt tabArray;
6634 if (textAttr.GetTabs().IsEmpty())
6635 tabArray = wxRichTextParagraph::GetDefaultTabs();
6636 else
6637 tabArray = textAttr.GetTabs();
ab14c7aa 6638
cfa3b256 6639 int tabCount = tabArray.GetCount();
41a85215 6640
cfa3b256 6641 for (int i = 0; i < tabCount; ++i)
61399247 6642 {
cfa3b256
JS
6643 int pos = tabArray[i];
6644 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6645 tabArray[i] = pos;
7f0d9d71 6646 }
41a85215 6647
cfa3b256 6648 int nextTabPos = -1;
61399247 6649
ab14c7aa
JS
6650 while (stringChunk.Find(wxT('\t')) >= 0)
6651 {
109bfc88
JS
6652 int absoluteWidth = 0;
6653
ab14c7aa 6654 // the string has a tab
7f0d9d71
JS
6655 // break up the string at the Tab
6656 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6657 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6658
31778480
JS
6659 if (partialExtents)
6660 {
109bfc88
JS
6661 int oldWidth;
6662 if (partialExtents->GetCount() > 0)
6663 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6664 else
6665 oldWidth = 0;
6666
31778480
JS
6667 // Add these partial extents
6668 wxArrayInt p;
6669 dc.GetPartialTextExtents(stringFragment, p);
6670 size_t j;
6671 for (j = 0; j < p.GetCount(); j++)
6672 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6673
6674 if (partialExtents->GetCount() > 0)
925a662a 6675 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6676 else
925a662a 6677 absoluteWidth = relativeX;
109bfc88
JS
6678 }
6679 else
6680 {
6681 dc.GetTextExtent(stringFragment, & w, & h);
6682 width += w;
603f702b 6683 absoluteWidth = width + relativeX;
109bfc88 6684 haveDescent = true;
31778480
JS
6685 }
6686
cfa3b256
JS
6687 bool notFound = true;
6688 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6689 {
cfa3b256 6690 nextTabPos = tabArray.Item(i);
4794d69c
JS
6691
6692 // Find the next tab position.
6693 // Even if we're at the end of the tab array, we must still process the chunk.
6694
6695 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6696 {
4794d69c
JS
6697 if (nextTabPos <= absoluteWidth)
6698 {
6699 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6700 nextTabPos = absoluteWidth + defaultTabWidth;
6701 }
6702
cfa3b256 6703 notFound = false;
925a662a 6704 width = nextTabPos - relativeX;
31778480
JS
6705
6706 if (partialExtents)
6707 partialExtents->Add(width);
7f0d9d71
JS
6708 }
6709 }
6710 }
6711 }
30bf7630 6712
31778480
JS
6713 if (!stringChunk.IsEmpty())
6714 {
31778480
JS
6715 if (partialExtents)
6716 {
109bfc88
JS
6717 int oldWidth;
6718 if (partialExtents->GetCount() > 0)
6719 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6720 else
6721 oldWidth = 0;
6722
31778480
JS
6723 // Add these partial extents
6724 wxArrayInt p;
6725 dc.GetPartialTextExtents(stringChunk, p);
6726 size_t j;
6727 for (j = 0; j < p.GetCount(); j++)
6728 partialExtents->Add(oldWidth + p[j]);
6729 }
109bfc88
JS
6730 else
6731 {
6732 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6733 width += w;
6734 haveDescent = true;
6735 }
6736 }
6737
6738 if (partialExtents)
6739 {
6740 int charHeight = dc.GetCharHeight();
6741 if ((*partialExtents).GetCount() > 0)
6742 w = (*partialExtents)[partialExtents->GetCount()-1];
6743 else
6744 w = 0;
6745 size = wxSize(w, charHeight);
6746 }
6747 else
6748 {
6749 size = wxSize(width, dc.GetCharHeight());
31778480 6750 }
30bf7630 6751
109bfc88
JS
6752 if (!haveDescent)
6753 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6754
30bf7630
JS
6755 if ( bScript )
6756 dc.SetFont(font);
6757
5d7836c4
JS
6758 return true;
6759}
6760
6761/// Do a split, returning an object containing the second part, and setting
6762/// the first part in 'this'.
6763wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6764{
ff76711f 6765 long index = pos - GetRange().GetStart();
3e541562 6766
28f92d74 6767 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6768 return NULL;
6769
6770 wxString firstPart = m_text.Mid(0, index);
6771 wxString secondPart = m_text.Mid(index);
6772
6773 m_text = firstPart;
6774
6775 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6776 newObject->SetAttributes(GetAttributes());
8db2e3ef 6777 newObject->SetProperties(GetProperties());
5d7836c4
JS
6778
6779 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6780 GetRange().SetEnd(pos-1);
3e541562 6781
5d7836c4
JS
6782 return newObject;
6783}
6784
6785/// Calculate range
6786void wxRichTextPlainText::CalculateRange(long start, long& end)
6787{
28f92d74 6788 end = start + m_text.length() - 1;
5d7836c4
JS
6789 m_range.SetRange(start, end);
6790}
6791
6792/// Delete range
6793bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6794{
6795 wxRichTextRange r = range;
6796
6797 r.LimitTo(GetRange());
6798
6799 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6800 {
6801 m_text.Empty();
6802 return true;
6803 }
6804
6805 long startIndex = r.GetStart() - GetRange().GetStart();
6806 long len = r.GetLength();
6807
6808 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6809 return true;
6810}
6811
6812/// Get text for the given range.
6813wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6814{
6815 wxRichTextRange r = range;
6816
6817 r.LimitTo(GetRange());
6818
6819 long startIndex = r.GetStart() - GetRange().GetStart();
6820 long len = r.GetLength();
6821
6822 return m_text.Mid(startIndex, len);
6823}
6824
6825/// Returns true if this object can merge itself with the given one.
6826bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
6827{
80a46597 6828 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
8db2e3ef 6829 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
5d7836c4
JS
6830}
6831
6832/// Returns true if this object merged itself with the given one.
6833/// The calling code will then delete the given object.
6834bool wxRichTextPlainText::Merge(wxRichTextObject* object)
6835{
6836 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
6837 wxASSERT( textObject != NULL );
6838
6839 if (textObject)
6840 {
6841 m_text += textObject->GetText();
99404ab0 6842 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
6843 return true;
6844 }
6845 else
6846 return false;
6847}
6848
6849/// Dump to output stream for debugging
6850void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
6851{
6852 wxRichTextObject::Dump(stream);
6853 stream << m_text << wxT("\n");
6854}
6855
ff76711f
JS
6856/// Get the first position from pos that has a line break character.
6857long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
6858{
6859 int i;
6860 int len = m_text.length();
6861 int startPos = pos - m_range.GetStart();
6862 for (i = startPos; i < len; i++)
6863 {
6864 wxChar ch = m_text[i];
6865 if (ch == wxRichTextLineBreakChar)
6866 {
6867 return i + m_range.GetStart();
6868 }
6869 }
6870 return -1;
6871}
6872
5d7836c4
JS
6873/*!
6874 * wxRichTextBuffer
6875 * This is a kind of box, used to represent the whole buffer
6876 */
6877
6878IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
6879
7c9fdebe
JS
6880wxList wxRichTextBuffer::sm_handlers;
6881wxList wxRichTextBuffer::sm_drawingHandlers;
6882wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
6883wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
6884int wxRichTextBuffer::sm_bulletRightMargin = 20;
6885float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
5d7836c4
JS
6886
6887/// Initialisation
6888void wxRichTextBuffer::Init()
6889{
6890 m_commandProcessor = new wxCommandProcessor;
6891 m_styleSheet = NULL;
6892 m_modified = false;
6893 m_batchedCommandDepth = 0;
6894 m_batchedCommand = NULL;
6895 m_suppressUndo = 0;
d2d0adc7 6896 m_handlerFlags = 0;
44219ff0 6897 m_scale = 1.0;
32423dd8
JS
6898 m_dimensionScale = 1.0;
6899 m_fontScale = 1.0;
f819ed5d 6900 SetMargins(4);
5d7836c4
JS
6901}
6902
6903/// Initialisation
6904wxRichTextBuffer::~wxRichTextBuffer()
6905{
6906 delete m_commandProcessor;
6907 delete m_batchedCommand;
6908
6909 ClearStyleStack();
d2d0adc7 6910 ClearEventHandlers();
5d7836c4
JS
6911}
6912
85d8909b 6913void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 6914{
85d8909b 6915 Reset();
3e541562 6916
5d7836c4 6917 GetCommandProcessor()->ClearCommands();
5d7836c4 6918
5d7836c4 6919 Modify(false);
1e967276 6920 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
6921}
6922
0ca07313
JS
6923void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
6924{
6925 wxRichTextParagraphLayoutBox::Copy(obj);
6926
6927 m_styleSheet = obj.m_styleSheet;
6928 m_modified = obj.m_modified;
bec80f4f
JS
6929 m_batchedCommandDepth = 0;
6930 if (m_batchedCommand)
6931 delete m_batchedCommand;
6932 m_batchedCommand = NULL;
0ca07313 6933 m_suppressUndo = obj.m_suppressUndo;
603f702b 6934 m_invalidRange = obj.m_invalidRange;
32423dd8
JS
6935 m_dimensionScale = obj.m_dimensionScale;
6936 m_fontScale = obj.m_fontScale;
0ca07313
JS
6937}
6938
38f833b1
JS
6939/// Push style sheet to top of stack
6940bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
6941{
6942 if (m_styleSheet)
6943 styleSheet->InsertSheet(m_styleSheet);
6944
6945 SetStyleSheet(styleSheet);
41a85215 6946
38f833b1
JS
6947 return true;
6948}
6949
6950/// Pop style sheet from top of stack
6951wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
6952{
6953 if (m_styleSheet)
6954 {
6955 wxRichTextStyleSheet* oldSheet = m_styleSheet;
6956 m_styleSheet = oldSheet->GetNextSheet();
6957 oldSheet->Unlink();
41a85215 6958
38f833b1
JS
6959 return oldSheet;
6960 }
6961 else
6962 return NULL;
6963}
6964
0ca07313
JS
6965/// Submit command to insert paragraphs
6966bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
6967{
4e63bfb9 6968 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
6969}
6970
6971/// Submit command to insert paragraphs
4e63bfb9 6972bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
6973{
6974 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 6975
0ca07313 6976 action->GetNewParagraphs() = paragraphs;
59509217 6977
0ca07313
JS
6978 action->SetPosition(pos);
6979
603f702b 6980 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
6981 if (!paragraphs.GetPartialParagraph())
6982 range.SetEnd(range.GetEnd()+1);
6983
0ca07313 6984 // Set the range we'll need to delete in Undo
99404ab0 6985 action->SetRange(range);
0ca07313 6986
603f702b 6987 buffer->SubmitAction(action);
0ca07313
JS
6988
6989 return true;
6990}
6991
5d7836c4 6992/// Submit command to insert the given text
fe5aa22c 6993bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 6994{
4e63bfb9 6995 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
6996}
6997
6998/// Submit command to insert the given text
4e63bfb9 6999bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7000{
7001 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7002
24777478
JS
7003 wxRichTextAttr* p = NULL;
7004 wxRichTextAttr paraAttr;
fe5aa22c
JS
7005 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7006 {
7c081bd2 7007 // Get appropriate paragraph style
603f702b 7008 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
7009 if (!paraAttr.IsDefault())
7010 p = & paraAttr;
7011 }
7012
fe5aa22c 7013 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 7014
603f702b 7015 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 7016
6636ef8d 7017 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
7018 {
7019 // Don't count the newline when undoing
7020 length --;
5d7836c4 7021 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 7022 }
6636ef8d 7023 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 7024 length --;
5d7836c4
JS
7025
7026 action->SetPosition(pos);
7027
7028 // Set the range we'll need to delete in Undo
0ca07313 7029 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 7030
603f702b 7031 buffer->SubmitAction(action);
7fe8059f 7032
5d7836c4
JS
7033 return true;
7034}
7035
7036/// Submit command to insert the given text
fe5aa22c 7037bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7038{
4e63bfb9 7039 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
7040}
7041
7042/// Submit command to insert the given text
4e63bfb9 7043bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7044{
7045 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7046
24777478
JS
7047 wxRichTextAttr* p = NULL;
7048 wxRichTextAttr paraAttr;
fe5aa22c
JS
7049 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7050 {
603f702b 7051 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
7052 if (!paraAttr.IsDefault())
7053 p = & paraAttr;
7054 }
7055
603f702b 7056 wxRichTextAttr attr(buffer->GetDefaultStyle());
32423dd8
JS
7057 // Don't include box attributes such as margins
7058 attr.GetTextBoxAttr().Reset();
7fe8059f
WS
7059
7060 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
7061 action->GetNewParagraphs().AppendChild(newPara);
7062 action->GetNewParagraphs().UpdateRanges();
7063 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
7064 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7065 long pos1 = pos;
7066
6c0ea513
JS
7067 if (p)
7068 newPara->SetAttributes(*p);
7069
c025e094
JS
7070 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7071 {
7072 if (para && para->GetRange().GetEnd() == pos)
7073 pos1 ++;
e2d0875a
JS
7074
7075 // Now see if we need to number the paragraph.
6c0ea513 7076 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
7077 {
7078 wxRichTextAttr numberingAttr;
7079 if (FindNextParagraphNumber(para, numberingAttr))
7080 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7081 }
c025e094
JS
7082 }
7083
5d7836c4
JS
7084 action->SetPosition(pos);
7085
99404ab0 7086 // Use the default character style
603f702b 7087 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
7088 {
7089 // Check whether the default style merely reflects the paragraph/basic style,
7090 // in which case don't apply it.
603f702b 7091 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
32423dd8 7092 defaultStyle.GetTextBoxAttr().Reset();
24777478 7093 wxRichTextAttr toApply;
c025e094
JS
7094 if (para)
7095 {
603f702b 7096 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 7097 wxRichTextAttr newAttr;
c025e094
JS
7098 // This filters out attributes that are accounted for by the current
7099 // paragraph/basic style
7100 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7101 }
7102 else
7103 toApply = defaultStyle;
7104
7105 if (!toApply.IsDefault())
7106 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7107 }
99404ab0 7108
5d7836c4 7109 // Set the range we'll need to delete in Undo
c025e094 7110 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 7111
603f702b 7112 buffer->SubmitAction(action);
7fe8059f 7113
5d7836c4
JS
7114 return true;
7115}
7116
7117/// Submit command to insert the given image
24777478
JS
7118bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7119 const wxRichTextAttr& textAttr)
5d7836c4 7120{
4e63bfb9 7121 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
7122}
7123
7124/// Submit command to insert the given image
4e63bfb9
JS
7125bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7126 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
7127 const wxRichTextAttr& textAttr)
7128{
7129 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7130
24777478
JS
7131 wxRichTextAttr* p = NULL;
7132 wxRichTextAttr paraAttr;
fe5aa22c
JS
7133 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7134 {
603f702b 7135 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
7136 if (!paraAttr.IsDefault())
7137 p = & paraAttr;
7138 }
7139
603f702b 7140 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 7141
32423dd8
JS
7142 // Don't include box attributes such as margins
7143 attr.GetTextBoxAttr().Reset();
7144
5d7836c4 7145 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
7146 if (p)
7147 newPara->SetAttributes(*p);
7148
5d7836c4
JS
7149 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7150 newPara->AppendChild(imageObject);
24777478 7151 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7152 action->GetNewParagraphs().AppendChild(newPara);
7153 action->GetNewParagraphs().UpdateRanges();
7154
7155 action->GetNewParagraphs().SetPartialParagraph(true);
7156
7157 action->SetPosition(pos);
7158
7159 // Set the range we'll need to delete in Undo
7160 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7161
603f702b 7162 buffer->SubmitAction(action);
7fe8059f 7163
5d7836c4
JS
7164 return true;
7165}
7166
cdaed652 7167// Insert an object with no change of it
603f702b
JS
7168wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7169{
4e63bfb9 7170 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7171}
7172
7173// Insert an object with no change of it
4e63bfb9 7174wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7175{
603f702b 7176 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7177
24777478
JS
7178 wxRichTextAttr* p = NULL;
7179 wxRichTextAttr paraAttr;
cdaed652
VZ
7180 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7181 {
603f702b 7182 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7183 if (!paraAttr.IsDefault())
7184 p = & paraAttr;
7185 }
7186
603f702b 7187 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652 7188
32423dd8
JS
7189 // Don't include box attributes such as margins
7190 attr.GetTextBoxAttr().Reset();
7191
cdaed652
VZ
7192 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7193 if (p)
7194 newPara->SetAttributes(*p);
7195
7196 newPara->AppendChild(object);
7197 action->GetNewParagraphs().AppendChild(newPara);
7198 action->GetNewParagraphs().UpdateRanges();
7199
7200 action->GetNewParagraphs().SetPartialParagraph(true);
7201
7202 action->SetPosition(pos);
7203
7204 // Set the range we'll need to delete in Undo
7205 action->SetRange(wxRichTextRange(pos, pos));
7206
603f702b 7207 buffer->SubmitAction(action);
cdaed652 7208
603f702b
JS
7209 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7210 return obj;
cdaed652 7211}
603f702b 7212
7c9fdebe
JS
7213wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7214 const wxRichTextProperties& properties,
7215 wxRichTextCtrl* ctrl, int flags,
7216 const wxRichTextAttr& textAttr)
7217{
7218 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7219
7220 wxRichTextAttr* p = NULL;
7221 wxRichTextAttr paraAttr;
7222 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7223 {
7224 paraAttr = GetStyleForNewParagraph(buffer, pos);
7225 if (!paraAttr.IsDefault())
7226 p = & paraAttr;
7227 }
7228
7229 wxRichTextAttr attr(buffer->GetDefaultStyle());
7230
32423dd8
JS
7231 // Don't include box attributes such as margins
7232 attr.GetTextBoxAttr().Reset();
7233
7c9fdebe
JS
7234 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7235 if (p)
7236 newPara->SetAttributes(*p);
7237
7238 wxRichTextField* fieldObject = new wxRichTextField();
7239 fieldObject->wxRichTextObject::SetProperties(properties);
7240 fieldObject->SetFieldType(fieldType);
7241 fieldObject->SetAttributes(textAttr);
7242 newPara->AppendChild(fieldObject);
7243 action->GetNewParagraphs().AppendChild(newPara);
7244 action->GetNewParagraphs().UpdateRanges();
7245 action->GetNewParagraphs().SetPartialParagraph(true);
7246 action->SetPosition(pos);
7247
7248 // Set the range we'll need to delete in Undo
7249 action->SetRange(wxRichTextRange(pos, pos));
7250
7251 buffer->SubmitAction(action);
7252
7253 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7254 return obj;
7255}
7256
fe5aa22c
JS
7257/// Get the style that is appropriate for a new paragraph at this position.
7258/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7259/// style.
603f702b 7260wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7261{
7262 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7263 if (para)
7264 {
24777478 7265 wxRichTextAttr attr;
d2d0adc7 7266 bool foundAttributes = false;
3e541562 7267
d2d0adc7 7268 // Look for a matching paragraph style
603f702b 7269 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7270 {
603f702b 7271 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7272 if (paraDef)
fe5aa22c 7273 {
caad0109
JS
7274 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7275 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7276 {
603f702b 7277 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7278 if (nextParaDef)
7279 {
7280 foundAttributes = true;
603f702b 7281 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7282 }
7283 }
3e541562 7284
d2d0adc7
JS
7285 // If we didn't find the 'next style', use this style instead.
7286 if (!foundAttributes)
7287 {
7288 foundAttributes = true;
603f702b 7289 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7290 }
fe5aa22c
JS
7291 }
7292 }
e2d0875a
JS
7293
7294 // Also apply list style if present
603f702b 7295 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7296 {
603f702b 7297 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7298 if (listDef)
7299 {
7300 int thisIndent = para->GetAttributes().GetLeftIndent();
7301 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7302
7303 // Apply the overall list style, and item style for this level
603f702b 7304 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7305 wxRichTextApplyStyle(attr, listStyle);
7306 attr.SetOutlineLevel(thisLevel);
7307 if (para->GetAttributes().HasBulletNumber())
7308 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7309 }
34b4899d 7310 }
e2d0875a 7311
d2d0adc7
JS
7312 if (!foundAttributes)
7313 {
7314 attr = para->GetAttributes();
7315 int flags = attr.GetFlags();
fe5aa22c 7316
d2d0adc7
JS
7317 // Eliminate character styles
7318 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7319 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7320 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7321 attr.SetFlags(flags);
7322 }
3e541562 7323
fe5aa22c
JS
7324 return attr;
7325 }
7326 else
24777478 7327 return wxRichTextAttr();
fe5aa22c
JS
7328}
7329
5d7836c4 7330/// Submit command to delete this range
12cc29c5 7331bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7332{
603f702b
JS
7333 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7334}
7335
7336/// Submit command to delete this range
7337bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7338{
7339 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7340
12cc29c5 7341 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7342
7343 // Set the range to delete
7344 action->SetRange(range);
7fe8059f 7345
5d7836c4
JS
7346 // Copy the fragment that we'll need to restore in Undo
7347 CopyFragment(range, action->GetOldParagraphs());
7348
6c0ea513
JS
7349 // See if we're deleting a paragraph marker, in which case we need to
7350 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7351 if (range.GetStart() == range.GetEnd())
5d7836c4 7352 {
6c0ea513
JS
7353 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7354 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7355 {
6c0ea513
JS
7356 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7357 if (nextPara && nextPara != para)
5d7836c4 7358 {
6c0ea513
JS
7359 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7360 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7361 }
7362 }
7363 }
7364
603f702b 7365 buffer->SubmitAction(action);
7fe8059f 7366
5d7836c4
JS
7367 return true;
7368}
7369
7370/// Collapse undo/redo commands
7371bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7372{
7373 if (m_batchedCommandDepth == 0)
7374 {
7375 wxASSERT(m_batchedCommand == NULL);
7376 if (m_batchedCommand)
7377 {
0745f364 7378 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7379 }
7380 m_batchedCommand = new wxRichTextCommand(cmdName);
7381 }
7382
7fe8059f 7383 m_batchedCommandDepth ++;
5d7836c4
JS
7384
7385 return true;
7386}
7387
7388/// Collapse undo/redo commands
7389bool wxRichTextBuffer::EndBatchUndo()
7390{
7391 m_batchedCommandDepth --;
7392
7393 wxASSERT(m_batchedCommandDepth >= 0);
7394 wxASSERT(m_batchedCommand != NULL);
7395
7396 if (m_batchedCommandDepth == 0)
7397 {
0745f364 7398 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7399 m_batchedCommand = NULL;
7400 }
7401
7402 return true;
7403}
7404
7405/// Submit immediately, or delay according to whether collapsing is on
7406bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7407{
cc2aecde
JS
7408 if (action && !action->GetNewParagraphs().IsEmpty())
7409 PrepareContent(action->GetNewParagraphs());
7410
5d7836c4 7411 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7412 {
7413 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7414 cmd->AddAction(action);
7415 cmd->Do();
7416 cmd->GetActions().Clear();
7417 delete cmd;
7418
5d7836c4 7419 m_batchedCommand->AddAction(action);
0745f364 7420 }
5d7836c4
JS
7421 else
7422 {
7423 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7424 cmd->AddAction(action);
7425
7426 // Only store it if we're not suppressing undo.
7427 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7428 }
7429
7430 return true;
7431}
7432
7433/// Begin suppressing undo/redo commands.
7434bool wxRichTextBuffer::BeginSuppressUndo()
7435{
7fe8059f 7436 m_suppressUndo ++;
5d7836c4
JS
7437
7438 return true;
7439}
7440
7441/// End suppressing undo/redo commands.
7442bool wxRichTextBuffer::EndSuppressUndo()
7443{
7fe8059f 7444 m_suppressUndo --;
5d7836c4
JS
7445
7446 return true;
7447}
7448
7449/// Begin using a style
24777478 7450bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7451{
24777478 7452 wxRichTextAttr newStyle(GetDefaultStyle());
32423dd8 7453 newStyle.GetTextBoxAttr().Reset();
5d7836c4
JS
7454
7455 // Save the old default style
32423dd8 7456 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
5d7836c4
JS
7457
7458 wxRichTextApplyStyle(newStyle, style);
7459 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7460
7461 SetDefaultStyle(newStyle);
7462
5d7836c4
JS
7463 return true;
7464}
7465
7466/// End the style
7467bool wxRichTextBuffer::EndStyle()
7468{
63886f6d 7469 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7470 {
7471 wxLogDebug(_("Too many EndStyle calls!"));
7472 return false;
7473 }
7474
09f14108 7475 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7476 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7477 m_attributeStack.Erase(node);
5d7836c4
JS
7478
7479 SetDefaultStyle(*attr);
7480
7481 delete attr;
7482 return true;
7483}
7484
7485/// End all styles
7486bool wxRichTextBuffer::EndAllStyles()
7487{
7488 while (m_attributeStack.GetCount() != 0)
7489 EndStyle();
7490 return true;
7491}
7492
7493/// Clear the style stack
7494void wxRichTextBuffer::ClearStyleStack()
7495{
09f14108 7496 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7497 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7498 m_attributeStack.Clear();
7499}
7500
7501/// Begin using bold
7502bool wxRichTextBuffer::BeginBold()
7503{
24777478 7504 wxRichTextAttr attr;
7d76fbd5 7505 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7506
5d7836c4
JS
7507 return BeginStyle(attr);
7508}
7509
7510/// Begin using italic
7511bool wxRichTextBuffer::BeginItalic()
7512{
24777478 7513 wxRichTextAttr attr;
7d76fbd5 7514 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7515
5d7836c4
JS
7516 return BeginStyle(attr);
7517}
7518
7519/// Begin using underline
7520bool wxRichTextBuffer::BeginUnderline()
7521{
24777478 7522 wxRichTextAttr attr;
44cc96a8 7523 attr.SetFontUnderlined(true);
7fe8059f 7524
5d7836c4
JS
7525 return BeginStyle(attr);
7526}
7527
7528/// Begin using point size
7529bool wxRichTextBuffer::BeginFontSize(int pointSize)
7530{
24777478 7531 wxRichTextAttr attr;
44cc96a8 7532 attr.SetFontSize(pointSize);
7fe8059f 7533
5d7836c4
JS
7534 return BeginStyle(attr);
7535}
7536
7537/// Begin using this font
7538bool wxRichTextBuffer::BeginFont(const wxFont& font)
7539{
24777478 7540 wxRichTextAttr attr;
5d7836c4 7541 attr.SetFont(font);
7fe8059f 7542
5d7836c4
JS
7543 return BeginStyle(attr);
7544}
7545
7546/// Begin using this colour
7547bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7548{
24777478 7549 wxRichTextAttr attr;
5d7836c4
JS
7550 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7551 attr.SetTextColour(colour);
7fe8059f 7552
5d7836c4
JS
7553 return BeginStyle(attr);
7554}
7555
7556/// Begin using alignment
7557bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7558{
24777478 7559 wxRichTextAttr attr;
5d7836c4
JS
7560 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7561 attr.SetAlignment(alignment);
7fe8059f 7562
5d7836c4
JS
7563 return BeginStyle(attr);
7564}
7565
7566/// Begin left indent
7567bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7568{
24777478 7569 wxRichTextAttr attr;
5d7836c4
JS
7570 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7571 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7572
5d7836c4
JS
7573 return BeginStyle(attr);
7574}
7575
7576/// Begin right indent
7577bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7578{
24777478 7579 wxRichTextAttr attr;
5d7836c4
JS
7580 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7581 attr.SetRightIndent(rightIndent);
7fe8059f 7582
5d7836c4
JS
7583 return BeginStyle(attr);
7584}
7585
7586/// Begin paragraph spacing
7587bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7588{
7589 long flags = 0;
7590 if (before != 0)
7591 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7592 if (after != 0)
7593 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7594
24777478 7595 wxRichTextAttr attr;
5d7836c4
JS
7596 attr.SetFlags(flags);
7597 attr.SetParagraphSpacingBefore(before);
7598 attr.SetParagraphSpacingAfter(after);
7fe8059f 7599
5d7836c4
JS
7600 return BeginStyle(attr);
7601}
7602
7603/// Begin line spacing
7604bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
7605{
24777478 7606 wxRichTextAttr attr;
5d7836c4
JS
7607 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
7608 attr.SetLineSpacing(lineSpacing);
7fe8059f 7609
5d7836c4
JS
7610 return BeginStyle(attr);
7611}
7612
7613/// Begin numbered bullet
7614bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
7615{
24777478 7616 wxRichTextAttr attr;
f089713f 7617 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7618 attr.SetBulletStyle(bulletStyle);
7619 attr.SetBulletNumber(bulletNumber);
7620 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7621
5d7836c4
JS
7622 return BeginStyle(attr);
7623}
7624
7625/// Begin symbol bullet
d2d0adc7 7626bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 7627{
24777478 7628 wxRichTextAttr attr;
f089713f 7629 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7630 attr.SetBulletStyle(bulletStyle);
7631 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 7632 attr.SetBulletText(symbol);
7fe8059f 7633
5d7836c4
JS
7634 return BeginStyle(attr);
7635}
7636
f089713f
JS
7637/// Begin standard bullet
7638bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
7639{
24777478 7640 wxRichTextAttr attr;
f089713f
JS
7641 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7642 attr.SetBulletStyle(bulletStyle);
7643 attr.SetLeftIndent(leftIndent, leftSubIndent);
7644 attr.SetBulletName(bulletName);
7645
7646 return BeginStyle(attr);
7647}
7648
5d7836c4
JS
7649/// Begin named character style
7650bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
7651{
7652 if (GetStyleSheet())
7653 {
7654 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7655 if (def)
7656 {
24777478 7657 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7658 return BeginStyle(attr);
7659 }
7660 }
7661 return false;
7662}
7663
7664/// Begin named paragraph style
7665bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
7666{
7667 if (GetStyleSheet())
7668 {
7669 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
7670 if (def)
7671 {
24777478 7672 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7673 return BeginStyle(attr);
7674 }
7675 }
7676 return false;
7677}
7678
f089713f
JS
7679/// Begin named list style
7680bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
7681{
7682 if (GetStyleSheet())
7683 {
7684 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
7685 if (def)
7686 {
24777478 7687 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
7688
7689 attr.SetBulletNumber(number);
7690
7691 return BeginStyle(attr);
7692 }
7693 }
7694 return false;
7695}
7696
d2d0adc7
JS
7697/// Begin URL
7698bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
7699{
24777478 7700 wxRichTextAttr attr;
d2d0adc7
JS
7701
7702 if (!characterStyle.IsEmpty() && GetStyleSheet())
7703 {
7704 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7705 if (def)
7706 {
336d8ae9 7707 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
7708 }
7709 }
7710 attr.SetURL(url);
7711
7712 return BeginStyle(attr);
7713}
7714
5d7836c4
JS
7715/// Adds a handler to the end
7716void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
7717{
7718 sm_handlers.Append(handler);
7719}
7720
7721/// Inserts a handler at the front
7722void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
7723{
7724 sm_handlers.Insert( handler );
7725}
7726
7727/// Removes a handler
7728bool wxRichTextBuffer::RemoveHandler(const wxString& name)
7729{
7730 wxRichTextFileHandler *handler = FindHandler(name);
7731 if (handler)
7732 {
7733 sm_handlers.DeleteObject(handler);
7734 delete handler;
7735 return true;
7736 }
7737 else
7738 return false;
7739}
7740
7741/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
7742wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
7743 wxRichTextFileType imageType)
5d7836c4
JS
7744{
7745 if (imageType != wxRICHTEXT_TYPE_ANY)
7746 return FindHandler(imageType);
0ca07313 7747 else if (!filename.IsEmpty())
5d7836c4
JS
7748 {
7749 wxString path, file, ext;
a51e601e 7750 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
7751 return FindHandler(ext, imageType);
7752 }
0ca07313
JS
7753 else
7754 return NULL;
5d7836c4
JS
7755}
7756
7757
7758/// Finds a handler by name
7759wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
7760{
7761 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7762 while (node)
7763 {
7764 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7765 if (handler->GetName().Lower() == name.Lower()) return handler;
7766
7767 node = node->GetNext();
7768 }
7769 return NULL;
7770}
7771
7772/// Finds a handler by extension and type
d75a69e8 7773wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
7774{
7775 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7776 while (node)
7777 {
7778 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7779 if ( handler->GetExtension().Lower() == extension.Lower() &&
7780 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
7781 return handler;
7782 node = node->GetNext();
7783 }
7784 return 0;
7785}
7786
7787/// Finds a handler by type
d75a69e8 7788wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
7789{
7790 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7791 while (node)
7792 {
7793 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
7794 if (handler->GetType() == type) return handler;
7795 node = node->GetNext();
7796 }
7797 return NULL;
7798}
7799
7800void wxRichTextBuffer::InitStandardHandlers()
7801{
7802 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
7803 AddHandler(new wxRichTextPlainTextHandler);
7804}
7805
7806void wxRichTextBuffer::CleanUpHandlers()
7807{
7808 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7809 while (node)
7810 {
7811 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
7812 wxList::compatibility_iterator next = node->GetNext();
7813 delete handler;
7814 node = next;
7815 }
7816
7817 sm_handlers.Clear();
7818}
7819
1e967276 7820wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 7821{
1e967276
JS
7822 if (types)
7823 types->Clear();
7824
5d7836c4
JS
7825 wxString wildcard;
7826
7827 wxList::compatibility_iterator node = GetHandlers().GetFirst();
7828 int count = 0;
7829 while (node)
7830 {
7831 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 7832 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
7833 {
7834 if (combine)
7835 {
7836 if (count > 0)
7837 wildcard += wxT(";");
7838 wildcard += wxT("*.") + handler->GetExtension();
7839 }
7840 else
7841 {
7842 if (count > 0)
7843 wildcard += wxT("|");
7844 wildcard += handler->GetName();
7845 wildcard += wxT(" ");
7846 wildcard += _("files");
7847 wildcard += wxT(" (*.");
7848 wildcard += handler->GetExtension();
7849 wildcard += wxT(")|*.");
7850 wildcard += handler->GetExtension();
1e967276
JS
7851 if (types)
7852 types->Add(handler->GetType());
5d7836c4
JS
7853 }
7854 count ++;
7855 }
7856
7857 node = node->GetNext();
7858 }
7859
7860 if (combine)
7861 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
7862 return wildcard;
7863}
7864
7865/// Load a file
d75a69e8 7866bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7867{
7868 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7869 if (handler)
1e967276 7870 {
24777478 7871 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7872 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7873 bool success = handler->LoadFile(this, filename);
7874 Invalidate(wxRICHTEXT_ALL);
7875 return success;
7876 }
5d7836c4
JS
7877 else
7878 return false;
7879}
7880
7881/// Save a file
d75a69e8 7882bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7883{
7884 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7885 if (handler)
d2d0adc7
JS
7886 {
7887 handler->SetFlags(GetHandlerFlags());
5d7836c4 7888 return handler->SaveFile(this, filename);
d2d0adc7 7889 }
5d7836c4
JS
7890 else
7891 return false;
7892}
7893
7894/// Load from a stream
d75a69e8 7895bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7896{
7897 wxRichTextFileHandler* handler = FindHandler(type);
7898 if (handler)
1e967276 7899 {
24777478 7900 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7901 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7902 bool success = handler->LoadFile(this, stream);
7903 Invalidate(wxRICHTEXT_ALL);
7904 return success;
7905 }
5d7836c4
JS
7906 else
7907 return false;
7908}
7909
7910/// Save to a stream
d75a69e8 7911bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7912{
7913 wxRichTextFileHandler* handler = FindHandler(type);
7914 if (handler)
d2d0adc7
JS
7915 {
7916 handler->SetFlags(GetHandlerFlags());
5d7836c4 7917 return handler->SaveFile(this, stream);
d2d0adc7 7918 }
5d7836c4
JS
7919 else
7920 return false;
7921}
7922
7923/// Copy the range to the clipboard
7924bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
7925{
7926 bool success = false;
603f702b
JS
7927 wxRichTextParagraphLayoutBox* container = this;
7928 if (GetRichTextCtrl())
7929 container = GetRichTextCtrl()->GetFocusObject();
7930
11ef729d 7931#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 7932
d2142335 7933 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 7934 {
0ca07313
JS
7935 wxTheClipboard->Clear();
7936
7937 // Add composite object
7938
7939 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
7940
7941 {
603f702b 7942 wxString text = container->GetTextForRange(range);
0ca07313
JS
7943
7944#ifdef __WXMSW__
7945 text = wxTextFile::Translate(text, wxTextFileType_Dos);
7946#endif
7947
7948 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
7949 }
7950
7951 // Add rich text buffer data object. This needs the XML handler to be present.
7952
7953 if (FindHandler(wxRICHTEXT_TYPE_XML))
7954 {
7955 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 7956 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
7957
7958 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
7959 }
7960
7961 if (wxTheClipboard->SetData(compositeObject))
7962 success = true;
7963
5d7836c4
JS
7964 wxTheClipboard->Close();
7965 }
0ca07313 7966
39a1c2f2
WS
7967#else
7968 wxUnusedVar(range);
7969#endif
5d7836c4
JS
7970 return success;
7971}
7972
7973/// Paste the clipboard content to the buffer
7974bool wxRichTextBuffer::PasteFromClipboard(long position)
7975{
7976 bool success = false;
603f702b
JS
7977 wxRichTextParagraphLayoutBox* container = this;
7978 if (GetRichTextCtrl())
7979 container = GetRichTextCtrl()->GetFocusObject();
7980
11ef729d 7981#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
7982 if (CanPasteFromClipboard())
7983 {
7984 if (wxTheClipboard->Open())
7985 {
0ca07313
JS
7986 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7987 {
7988 wxRichTextBufferDataObject data;
7989 wxTheClipboard->GetData(data);
7990 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
7991 if (richTextBuffer)
7992 {
4e63bfb9 7993 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 7994 if (GetRichTextCtrl())
603f702b 7995 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
7996 delete richTextBuffer;
7997 }
7998 }
f7d83f24 7999 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
8000 #if wxUSE_UNICODE
8001 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8002 #endif
f7d83f24 8003 )
5d7836c4
JS
8004 {
8005 wxTextDataObject data;
8006 wxTheClipboard->GetData(data);
8007 wxString text(data.GetText());
c21f3211
JS
8008#ifdef __WXMSW__
8009 wxString text2;
8010 text2.Alloc(text.Length()+1);
8011 size_t i;
8012 for (i = 0; i < text.Length(); i++)
8013 {
8014 wxChar ch = text[i];
8015 if (ch != wxT('\r'))
8016 text2 += ch;
8017 }
8018#else
8019 wxString text2 = text;
8020#endif
4e63bfb9 8021 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 8022
62381daa
JS
8023 if (GetRichTextCtrl())
8024 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8025
5d7836c4
JS
8026 success = true;
8027 }
8028 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8029 {
8030 wxBitmapDataObject data;
8031 wxTheClipboard->GetData(data);
8032 wxBitmap bitmap(data.GetBitmap());
8033 wxImage image(bitmap.ConvertToImage());
8034
603f702b 8035 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 8036
5d7836c4
JS
8037 action->GetNewParagraphs().AddImage(image);
8038
8039 if (action->GetNewParagraphs().GetChildCount() == 1)
8040 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 8041
9c8e10ad 8042 action->SetPosition(position+1);
7fe8059f 8043
5d7836c4 8044 // Set the range we'll need to delete in Undo
9c8e10ad 8045 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 8046
5d7836c4
JS
8047 SubmitAction(action);
8048
8049 success = true;
8050 }
8051 wxTheClipboard->Close();
8052 }
8053 }
39a1c2f2
WS
8054#else
8055 wxUnusedVar(position);
8056#endif
5d7836c4
JS
8057 return success;
8058}
8059
8060/// Can we paste from the clipboard?
8061bool wxRichTextBuffer::CanPasteFromClipboard() const
8062{
7fe8059f 8063 bool canPaste = false;
11ef729d 8064#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 8065 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 8066 {
f7d83f24
VZ
8067 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8068#if wxUSE_UNICODE
603f702b
JS
8069 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8070#endif
8071 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8072 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 8073 {
7fe8059f 8074 canPaste = true;
5d7836c4
JS
8075 }
8076 wxTheClipboard->Close();
8077 }
39a1c2f2 8078#endif
5d7836c4
JS
8079 return canPaste;
8080}
8081
8082/// Dumps contents of buffer for debugging purposes
8083void wxRichTextBuffer::Dump()
8084{
8085 wxString text;
8086 {
8087 wxStringOutputStream stream(& text);
8088 wxTextOutputStream textStream(stream);
8089 Dump(textStream);
8090 }
8091
8092 wxLogDebug(text);
8093}
8094
d2d0adc7
JS
8095/// Add an event handler
8096bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8097{
8098 m_eventHandlers.Append(handler);
8099 return true;
8100}
8101
8102/// Remove an event handler
8103bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8104{
8105 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8106 if (node)
8107 {
8108 m_eventHandlers.Erase(node);
8109 if (deleteHandler)
8110 delete handler;
3e541562 8111
d2d0adc7
JS
8112 return true;
8113 }
8114 else
8115 return false;
8116}
8117
8118/// Clear event handlers
8119void wxRichTextBuffer::ClearEventHandlers()
8120{
8121 m_eventHandlers.Clear();
8122}
8123
8124/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8125/// otherwise will stop at the first successful one.
8126bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8127{
8128 bool success = false;
8129 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8130 {
8131 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8132 if (handler->ProcessEvent(event))
8133 {
8134 success = true;
8135 if (!sendToAll)
8136 return true;
8137 }
8138 }
8139 return success;
8140}
8141
8142/// Set style sheet and notify of the change
8143bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8144{
8145 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 8146
616c7cbd 8147 wxWindowID winid = wxID_ANY;
d2d0adc7 8148 if (GetRichTextCtrl())
616c7cbd 8149 winid = GetRichTextCtrl()->GetId();
3e541562 8150
616c7cbd 8151 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 8152 event.SetEventObject(GetRichTextCtrl());
603f702b 8153 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
8154 event.SetOldStyleSheet(oldSheet);
8155 event.SetNewStyleSheet(sheet);
8156 event.Allow();
3e541562 8157
d2d0adc7
JS
8158 if (SendEvent(event) && !event.IsAllowed())
8159 {
8160 if (sheet != oldSheet)
8161 delete sheet;
8162
8163 return false;
8164 }
8165
8166 if (oldSheet && oldSheet != sheet)
8167 delete oldSheet;
8168
8169 SetStyleSheet(sheet);
8170
8171 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8172 event.SetOldStyleSheet(NULL);
8173 event.Allow();
8174
8175 return SendEvent(event);
8176}
8177
8178/// Set renderer, deleting old one
8179void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8180{
8181 if (sm_renderer)
8182 delete sm_renderer;
8183 sm_renderer = renderer;
8184}
8185
603f702b
JS
8186/// Hit-testing: returns a flag indicating hit test details, plus
8187/// information about position
8db2e3ef 8188int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 8189{
8db2e3ef 8190 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
8191 if (ret != wxRICHTEXT_HITTEST_NONE)
8192 {
8193 return ret;
8194 }
8195 else
8196 {
8197 textPosition = m_ownRange.GetEnd()-1;
8198 *obj = this;
8199 *contextObj = this;
8200 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8201 }
8202}
8203
32423dd8
JS
8204void wxRichTextBuffer::SetFontScale(double fontScale)
8205{
8206 m_fontScale = fontScale;
8207 m_fontTable.SetFontScale(fontScale);
8208}
8209
8210void wxRichTextBuffer::SetDimensionScale(double dimScale)
8211{
8212 m_dimensionScale = dimScale;
8213}
8214
24777478 8215bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8216{
a1b806b9 8217 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8218 {
ecb5fbf1
JS
8219 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8220 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8221 }
8222 else
8223 {
ecb5fbf1
JS
8224 wxCheckSetPen(dc, *wxBLACK_PEN);
8225 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8226 }
8227
8228 wxFont font;
44cc96a8
JS
8229 if (bulletAttr.HasFont())
8230 {
8231 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8232 }
d2d0adc7
JS
8233 else
8234 font = (*wxNORMAL_FONT);
8235
ecb5fbf1 8236 wxCheckSetFont(dc, font);
d2d0adc7
JS
8237
8238 int charHeight = dc.GetCharHeight();
3e541562 8239
d2d0adc7
JS
8240 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8241 int bulletHeight = bulletWidth;
8242
8243 int x = rect.x;
3e541562 8244
d2d0adc7
JS
8245 // Calculate the top position of the character (as opposed to the whole line height)
8246 int y = rect.y + (rect.height - charHeight);
3e541562 8247
d2d0adc7
JS
8248 // Calculate where the bullet should be positioned
8249 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8250
d2d0adc7 8251 // The margin between a bullet and text.
44219ff0 8252 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8253
d2d0adc7
JS
8254 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8255 x = rect.x + rect.width - bulletWidth - margin;
8256 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8257 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8258
d2d0adc7
JS
8259 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8260 {
8261 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8262 }
8263 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8264 {
8265 wxPoint pts[5];
8266 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8267 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8268 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8269 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8270
d2d0adc7
JS
8271 dc.DrawPolygon(4, pts);
8272 }
8273 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8274 {
8275 wxPoint pts[3];
8276 pts[0].x = x; pts[0].y = y;
8277 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8278 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8279
d2d0adc7
JS
8280 dc.DrawPolygon(3, pts);
8281 }
603f702b
JS
8282 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8283 {
8284 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8285 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8286 }
d2d0adc7
JS
8287 else // "standard/circle", and catch-all
8288 {
8289 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8290 }
8291
d2d0adc7
JS
8292 return true;
8293}
8294
24777478 8295bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8296{
8297 if (!text.empty())
8298 {
8299 wxFont font;
44cc96a8
JS
8300 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8301 {
24777478 8302 wxRichTextAttr fontAttr;
32423dd8
JS
8303 if (attr.HasFontPixelSize())
8304 fontAttr.SetFontPixelSize(attr.GetFontSize());
8305 else
8306 fontAttr.SetFontPointSize(attr.GetFontSize());
44cc96a8
JS
8307 fontAttr.SetFontStyle(attr.GetFontStyle());
8308 fontAttr.SetFontWeight(attr.GetFontWeight());
8309 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8310 fontAttr.SetFontFaceName(attr.GetBulletFont());
8311 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8312 }
8313 else if (attr.HasFont())
8314 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8315 else
8316 font = (*wxNORMAL_FONT);
8317
ecb5fbf1 8318 wxCheckSetFont(dc, font);
d2d0adc7 8319
a1b806b9 8320 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8321 dc.SetTextForeground(attr.GetTextColour());
8322
04ee05f9 8323 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8324
8325 int charHeight = dc.GetCharHeight();
8326 wxCoord tw, th;
8327 dc.GetTextExtent(text, & tw, & th);
8328
8329 int x = rect.x;
8330
8331 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8332 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8333
8334 // The margin between a bullet and text.
44219ff0 8335 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8336
d2d0adc7
JS
8337 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8338 x = (rect.x + rect.width) - tw - margin;
8339 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8340 x = x + (rect.width)/2 - tw/2;
8341
8342 dc.DrawText(text, x, y);
3e541562 8343
d2d0adc7
JS
8344 return true;
8345 }
8346 else
8347 return false;
8348}
8349
24777478 8350bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8351{
8352 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8353 // with the buffer. The store will allow retrieval from memory, disk or other means.
8354 return false;
8355}
8356
8357/// Enumerate the standard bullet names currently supported
8358bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8359{
04529b2a 8360 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8361 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8362 bulletNames.Add(wxTRANSLATE("standard/square"));
8363 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8364 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8365
8366 return true;
8367}
5d7836c4 8368
bec80f4f
JS
8369/*!
8370 * wxRichTextBox
8371 */
8372
603f702b 8373IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8374
8375wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8376 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8377{
8378}
8379
8380/// Draw the item
8db2e3ef 8381bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8382{
603f702b
JS
8383 if (!IsShown())
8384 return true;
5ad9ae3a 8385
603f702b
JS
8386 // TODO: if the active object in the control, draw an indication.
8387 // We need to add the concept of active object, and not just focus object,
8388 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8389 // Ultimately we would like to be able to interactively resize an active object
8390 // using drag handles.
8db2e3ef 8391 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8392}
5ad9ae3a 8393
603f702b
JS
8394/// Copy
8395void wxRichTextBox::Copy(const wxRichTextBox& obj)
8396{
8397 wxRichTextParagraphLayoutBox::Copy(obj);
8398}
8399
8400// Edit properties via a GUI
8401bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8402{
8403 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8404 boxDlg.SetAttributes(GetAttributes());
8405
8406 if (boxDlg.ShowModal() == wxID_OK)
8407 {
8408 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8409 // indeterminate in the object.
8410 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8411 return true;
5ad9ae3a 8412 }
603f702b
JS
8413 else
8414 return false;
bec80f4f
JS
8415}
8416
7c9fdebe
JS
8417/*!
8418 * wxRichTextField
8419 */
8420
8421IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8422
8423wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8424 wxRichTextParagraphLayoutBox(parent)
8425{
8426 SetFieldType(fieldType);
8427}
8428
8429/// Draw the item
8430bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8431{
8432 if (!IsShown())
8433 return true;
8434
8435 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8436 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8437 return true;
8438
8439 // Fallback; but don't draw guidelines.
8440 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8441 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8442}
8443
8444bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8445{
8446 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8447 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8448 return true;
8449
8450 // Fallback
8451 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8452}
8453
8454bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8455{
8456 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8457 if (fieldType)
8458 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents);
8459
8460 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
8461}
8462
8463/// Calculate range
8464void wxRichTextField::CalculateRange(long start, long& end)
8465{
8466 if (IsTopLevel())
8467 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8468 else
8469 wxRichTextObject::CalculateRange(start, end);
8470}
8471
8472/// Copy
8473void wxRichTextField::Copy(const wxRichTextField& obj)
8474{
8475 wxRichTextParagraphLayoutBox::Copy(obj);
8476
32423dd8 8477 UpdateField(GetBuffer());
7c9fdebe
JS
8478}
8479
8480// Edit properties via a GUI
8481bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8482{
8483 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8484 if (fieldType)
8485 return fieldType->EditProperties(this, parent, buffer);
8486
8487 return false;
8488}
8489
8490bool wxRichTextField::CanEditProperties() const
8491{
8492 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8493 if (fieldType)
8494 return fieldType->CanEditProperties((wxRichTextField*) this);
8495
8496 return false;
8497}
8498
8499wxString wxRichTextField::GetPropertiesMenuLabel() const
8500{
8501 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8502 if (fieldType)
8503 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8504
8505 return wxEmptyString;
8506}
8507
32423dd8 8508bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
7c9fdebe
JS
8509{
8510 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8511 if (fieldType)
32423dd8 8512 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
7c9fdebe
JS
8513
8514 return false;
8515}
8516
8517bool wxRichTextField::IsTopLevel() const
8518{
8519 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8520 if (fieldType)
8521 return fieldType->IsTopLevel((wxRichTextField*) this);
8522
8523 return true;
8524}
8525
8526IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8527
8528IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8529
8530wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8531{
8532 Init();
8533
8534 SetName(name);
8535 SetLabel(label);
8536 SetDisplayStyle(displayStyle);
8537}
8538
8539wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8540{
8541 Init();
8542
8543 SetName(name);
8544 SetBitmap(bitmap);
8545 SetDisplayStyle(displayStyle);
8546}
8547
8548void wxRichTextFieldTypeStandard::Init()
8549{
8550 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8551 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8552 m_textColour = *wxWHITE;
8553 m_borderColour = *wxBLACK;
8554 m_backgroundColour = *wxBLACK;
8555 m_verticalPadding = 1;
8556 m_horizontalPadding = 3;
8557 m_horizontalMargin = 2;
8558 m_verticalMargin = 0;
8559}
8560
8561void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
8562{
8563 wxRichTextFieldType::Copy(field);
8564
8565 m_label = field.m_label;
8566 m_displayStyle = field.m_displayStyle;
8567 m_font = field.m_font;
8568 m_textColour = field.m_textColour;
8569 m_borderColour = field.m_borderColour;
8570 m_backgroundColour = field.m_backgroundColour;
8571 m_verticalPadding = field.m_verticalPadding;
8572 m_horizontalPadding = field.m_horizontalPadding;
8573 m_horizontalMargin = field.m_horizontalMargin;
8574 m_bitmap = field.m_bitmap;
8575}
8576
8577bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
8578{
8579 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8580 return false; // USe default composite drawing
8581 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8582 {
8583 int borderSize = 1;
8584
8585 wxPen borderPen(m_borderColour, 1, wxSOLID);
8586 wxBrush backgroundBrush(m_backgroundColour);
8587 wxColour textColour(m_textColour);
8588
8589 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8590 {
8591 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
8592 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
8593
8594 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
8595 backgroundBrush = wxBrush(highlightColour);
8596
8597 wxCheckSetBrush(dc, backgroundBrush);
8598 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
8599 dc.DrawRectangle(rect);
8600 }
8601
8602 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
8603 borderSize = 0;
8604
8605 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8606 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
8607 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
8608
8609 // clientArea is where the text is actually written
8610 wxRect clientArea = objectRect;
8611
8612 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
8613 {
8614 dc.SetPen(borderPen);
8615 dc.SetBrush(backgroundBrush);
8616 dc.DrawRoundedRectangle(objectRect, 4.0);
8617 }
8618 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
8619 {
8620 int arrowLength = objectRect.height/2;
8621 clientArea.width -= (arrowLength - m_horizontalPadding);
8622
8623 wxPoint pts[5];
8624 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
8625 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
8626 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
8627 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
8628 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
8629 dc.SetPen(borderPen);
8630 dc.SetBrush(backgroundBrush);
8631 dc.DrawPolygon(5, pts);
8632 }
8633 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
8634 {
8635 int arrowLength = objectRect.height/2;
8636 clientArea.width -= (arrowLength - m_horizontalPadding);
8637 clientArea.x += (arrowLength - m_horizontalPadding);
8638
8639 wxPoint pts[5];
8640 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
8641 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
8642 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
8643 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
8644 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
8645 dc.SetPen(borderPen);
8646 dc.SetBrush(backgroundBrush);
8647 dc.DrawPolygon(5, pts);
8648 }
8649
8650 if (m_bitmap.IsOk())
8651 {
8652 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
8653 int y = clientArea.y + m_verticalPadding;
8654 dc.DrawBitmap(m_bitmap, x, y, true);
8655
8656 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8657 {
8658 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8659 wxCheckSetPen(dc, *wxBLACK_PEN);
8660 dc.SetLogicalFunction(wxINVERT);
8661 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
8662 dc.SetLogicalFunction(wxCOPY);
8663 }
8664 }
8665 else
8666 {
8667 wxString label(m_label);
8668 if (label.IsEmpty())
8669 label = wxT("??");
8670 int w, h, maxDescent;
8671 dc.SetFont(m_font);
8672 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
8673 dc.SetTextForeground(textColour);
8674
8675 int x = clientArea.x + (clientArea.width - w)/2;
8676 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
8677 dc.DrawText(m_label, x, y);
8678 }
8679 }
8680
8681 return true;
8682}
8683
8684bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
8685{
8686 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8687 return false; // USe default composite layout
8688
8689 wxSize size = GetSize(obj, dc, context, style);
8690 obj->SetCachedSize(size);
8691 obj->SetMinSize(size);
8692 obj->SetMaxSize(size);
8693 return true;
8694}
8695
8696bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8697{
8698 if (IsTopLevel(obj))
8699 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position);
8700 else
8701 {
8702 wxSize sz = GetSize(obj, dc, context, 0);
8703 if (partialExtents)
8704 {
8705 int lastSize;
8706 if (partialExtents->GetCount() > 0)
8707 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
8708 else
8709 lastSize = 0;
8710 partialExtents->Add(lastSize + sz.x);
8711 }
8712 size = sz;
8713 return true;
8714 }
8715}
8716
8717wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
8718{
8719 int borderSize = 1;
8720 int w = 0, h = 0, maxDescent = 0;
8721
8722 wxSize sz;
8723 if (m_bitmap.IsOk())
8724 {
8725 w = m_bitmap.GetWidth();
8726 h = m_bitmap.GetHeight();
8727
8728 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
8729 }
8730 else
8731 {
8732 wxString label(m_label);
8733 if (label.IsEmpty())
8734 label = wxT("??");
8735 dc.SetFont(m_font);
8736 dc.GetTextExtent(label, & w, &h, & maxDescent);
8737
8738 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
8739 }
8740
8741 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
8742 {
8743 sz.x += borderSize*2;
8744 sz.y += borderSize*2;
8745 }
8746
8747 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
8748 {
8749 // Add space for the arrow
8750 sz.x += (sz.y/2 - m_horizontalPadding);
8751 }
8752
8753 return sz;
8754}
8755
603f702b
JS
8756IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
8757
8758wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
8759 wxRichTextBox(parent)
bec80f4f 8760{
603f702b
JS
8761}
8762
8763/// Draw the item
8db2e3ef 8764bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 8765{
8db2e3ef 8766 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
8767}
8768
8769/// Copy
8770void wxRichTextCell::Copy(const wxRichTextCell& obj)
8771{
8772 wxRichTextBox::Copy(obj);
8773}
8774
8775// Edit properties via a GUI
8776bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8777{
8778 // We need to gather common attributes for all selected cells.
8779
8780 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
8781 bool multipleCells = false;
8782 wxRichTextAttr attr;
8783
8784 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
8785 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 8786 {
603f702b
JS
8787 wxRichTextAttr clashingAttr, absentAttr;
8788 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8789 size_t i;
8790 int selectedCellCount = 0;
8791 for (i = 0; i < sel.GetCount(); i++)
8792 {
8793 const wxRichTextRange& range = sel[i];
8794 wxRichTextCell* cell = table->GetCell(range.GetStart());
8795 if (cell)
8796 {
8797 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 8798
603f702b
JS
8799 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
8800
8801 selectedCellCount ++;
8802 }
8803 }
8804 multipleCells = selectedCellCount > 1;
5ad9ae3a 8805 }
603f702b
JS
8806 else
8807 {
8808 attr = GetAttributes();
8809 }
8810
8811 wxString caption;
8812 if (multipleCells)
8813 caption = _("Multiple Cell Properties");
8814 else
8815 caption = _("Cell Properties");
8816
8817 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
8818 cellDlg.SetAttributes(attr);
8819
80a46597 8820 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
603f702b
JS
8821 if (sizePage)
8822 {
8823 // We don't want position and floating controls for a cell.
8824 sizePage->ShowPositionControls(false);
8825 sizePage->ShowFloatingControls(false);
8826 }
8827
8828 if (cellDlg.ShowModal() == wxID_OK)
8829 {
8830 if (multipleCells)
8831 {
8832 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8833 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8834 // since it may represent clashing attributes across multiple objects.
8835 table->SetCellStyle(sel, attr);
8836 }
8837 else
8838 // For a single object, indeterminate attributes set by the user should be reflected in the
8839 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8840 // the style directly instead of applying (which ignores indeterminate attributes,
8841 // leaving them as they were).
8842 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8843 return true;
8844 }
8845 else
8846 return false;
8847}
8848
8849WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
8850
8851IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
8852
8853wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
8854{
8855 m_rowCount = 0;
8856 m_colCount = 0;
8857}
5ad9ae3a 8858
603f702b 8859// Draws the object.
8db2e3ef 8860bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 8861{
8db2e3ef 8862 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
8863}
8864
603f702b
JS
8865WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
8866WX_DEFINE_OBJARRAY(wxRichTextRectArray);
8867
8868// Lays the object out. rect is the space available for layout. Often it will
8869// be the specified overall space for this object, if trying to constrain
8870// layout to a particular size, or it could be the total space available in the
8871// parent. rect is the overall size, so we must subtract margins and padding.
8872// to get the actual available space.
8db2e3ef 8873bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 8874{
603f702b
JS
8875 SetPosition(rect.GetPosition());
8876
8877 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8878 // minimum size if within alloted size, then divide up remaining size
8879 // between rows/cols.
8880
8881 double scale = 1.0;
8882 wxRichTextBuffer* buffer = GetBuffer();
8883 if (buffer) scale = buffer->GetScale();
8884
8db2e3ef 8885 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
8886 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
8887
8db2e3ef
JS
8888 wxRichTextAttr attr(GetAttributes());
8889 context.ApplyVirtualAttributes(attr, this);
8890
603f702b
JS
8891 // If we have no fixed table size, and assuming we're not pushed for
8892 // space, then we don't have to try to stretch the table to fit the contents.
8893 bool stretchToFitTableWidth = false;
8894
8895 int tableWidth = rect.width;
8db2e3ef 8896 if (attr.GetTextBoxAttr().GetWidth().IsValid())
603f702b 8897 {
8db2e3ef 8898 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
8899
8900 // Fixed table width, so we do want to stretch columns out if necessary.
8901 stretchToFitTableWidth = true;
8902
8903 // Shouldn't be able to exceed the size passed to this function
8904 tableWidth = wxMin(rect.width, tableWidth);
8905 }
8906
8907 // Get internal padding
36307fdf 8908 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
8909 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8910 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
8911 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8912 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
8913
8914 // Assume that left and top padding are also used for inter-cell padding.
8915 int paddingX = paddingLeft;
8916 int paddingY = paddingTop;
8917
8918 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 8919 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
8920
8921 // Internal table width - the area for content
8922 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
8923
8924 int rowCount = m_cells.GetCount();
8925 if (m_colCount == 0 || rowCount == 0)
8926 {
8927 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
8928 SetCachedSize(overallRect.GetSize());
8929
8930 // Zero content size
8931 SetMinSize(overallRect.GetSize());
8932 SetMaxSize(GetMinSize());
8933 return true;
8934 }
8935
8936 // The final calculated widths
bb7bbd12
JS
8937 wxArrayInt colWidths;
8938 colWidths.Add(0, m_colCount);
603f702b 8939
bb7bbd12
JS
8940 wxArrayInt absoluteColWidths;
8941 absoluteColWidths.Add(0, m_colCount);
7c9fdebe 8942
bb7bbd12
JS
8943 wxArrayInt percentageColWidths;
8944 percentageColWidths.Add(0, m_colCount);
603f702b
JS
8945 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8946 // These are only relevant when the first column contains spanning information.
8947 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
8948 wxArrayInt maxColWidths;
8949 maxColWidths.Add(0, m_colCount);
8950 wxArrayInt minColWidths;
8951 minColWidths.Add(0, m_colCount);
603f702b
JS
8952
8953 wxSize tableSize(tableWidth, 0);
8954
8955 int i, j, k;
8956
8957 for (i = 0; i < m_colCount; i++)
8958 {
8959 absoluteColWidths[i] = 0;
8960 // absoluteColWidthsSpanning[i] = 0;
8961 percentageColWidths[i] = -1;
8962 // percentageColWidthsSpanning[i] = -1;
8963 colWidths[i] = 0;
8964 maxColWidths[i] = 0;
8965 minColWidths[i] = 0;
8966 // columnSpans[i] = 1;
8967 }
8968
8969 // (0) Determine which cells are visible according to spans
8970 // 1 2 3 4 5
8971 // __________________
8972 // | | | | | 1
8973 // |------| |----|
8974 // |------| | | 2
8975 // |------| | | 3
8976 // |------------------|
8977 // |__________________| 4
8978
8979 // To calculate cell visibility:
8980 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8981 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8982 // that cell, hide the cell.
8983
8984 // We can also use this array to match the size of spanning cells to the grid. Or just do
8985 // this when we iterate through all cells.
8986
8987 // 0.1: add spanning cells to an array
8988 wxRichTextRectArray rectArray;
8989 for (j = 0; j < m_rowCount; j++)
8990 {
8991 for (i = 0; i < m_colCount; i++)
8992 {
8993 wxRichTextBox* cell = GetCell(j, i);
8994 int colSpan = 1, rowSpan = 1;
8995 if (cell->GetProperties().HasProperty(wxT("colspan")))
8996 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8997 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8998 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8999 if (colSpan > 1 || rowSpan > 1)
9000 {
9001 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9002 }
9003 }
9004 }
9005 // 0.2: find which cells are subsumed by a spanning cell
9006 for (j = 0; j < m_rowCount; j++)
9007 {
9008 for (i = 0; i < m_colCount; i++)
9009 {
9010 wxRichTextBox* cell = GetCell(j, i);
9011 if (rectArray.GetCount() == 0)
9012 {
9013 cell->Show(true);
9014 }
9015 else
9016 {
9017 int colSpan = 1, rowSpan = 1;
9018 if (cell->GetProperties().HasProperty(wxT("colspan")))
9019 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9020 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9021 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9022 if (colSpan > 1 || rowSpan > 1)
9023 {
9024 // Assume all spanning cells are shown
9025 cell->Show(true);
9026 }
9027 else
9028 {
9029 bool shown = true;
9030 for (k = 0; k < (int) rectArray.GetCount(); k++)
9031 {
9032 if (rectArray[k].Contains(wxPoint(i, j)))
9033 {
9034 shown = false;
9035 break;
9036 }
9037 }
9038 cell->Show(shown);
9039 }
9040 }
9041 }
9042 }
9043
9044 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9045 // overlap with a spanned cell starting at a previous column position.
9046 // This means we need to keep an array of rects so we can check. However
9047 // it does also mean that some spans simply may not be taken into account
9048 // where there are different spans happening on different rows. In these cases,
9049 // they will simply be as wide as their constituent columns.
9050
9051 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9052 // the absolute or percentage width of each column.
9053
9054 for (j = 0; j < m_rowCount; j++)
9055 {
9056 // First get the overall margins so we can calculate percentage widths based on
9057 // the available content space for all cells on the row
9058
9059 int overallRowContentMargin = 0;
9060 int visibleCellCount = 0;
9061
9062 for (i = 0; i < m_colCount; i++)
9063 {
9064 wxRichTextBox* cell = GetCell(j, i);
9065 if (cell->IsShown())
9066 {
9067 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9068 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9069
9070 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9071 visibleCellCount ++;
9072 }
9073 }
9074
9075 // Add in inter-cell padding
9076 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9077
9078 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9079 wxSize rowTableSize(rowContentWidth, 0);
9080 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9081
9082 for (i = 0; i < m_colCount; i++)
9083 {
9084 wxRichTextBox* cell = GetCell(j, i);
9085 if (cell->IsShown())
9086 {
9087 int colSpan = 1;
9088 if (cell->GetProperties().HasProperty(wxT("colspan")))
9089 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9090
9091 // Lay out cell to find min/max widths
9092 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9093 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
9094
9095 if (colSpan == 1)
9096 {
9097 int absoluteCellWidth = -1;
9098 int percentageCellWidth = -1;
9099
9100 // I think we need to calculate percentages from the internal table size,
9101 // minus the padding between cells which we'll need to calculate from the
9102 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9103 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9104 // so if we want to conform to that we'll need to add in the overall cell margins.
9105 // However, this will make it difficult to specify percentages that add up to
9106 // 100% and still fit within the table width.
9107 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9108 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9109 // If we're using internal content size for the width, we would calculate the
9110 // the overall cell width for n cells as:
9111 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9112 // + thisOverallCellMargin
9113 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9114 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9115
9116 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9117 {
9118 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9119 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9120 {
9121 percentageCellWidth = w;
9122 }
9123 else
9124 {
9125 absoluteCellWidth = w;
9126 }
9127 // Override absolute width with minimum width if necessary
9128 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9129 absoluteCellWidth = cell->GetMinSize().x;
9130 }
9131
9132 if (absoluteCellWidth != -1)
9133 {
9134 if (absoluteCellWidth > absoluteColWidths[i])
9135 absoluteColWidths[i] = absoluteCellWidth;
9136 }
9137
9138 if (percentageCellWidth != -1)
9139 {
9140 if (percentageCellWidth > percentageColWidths[i])
9141 percentageColWidths[i] = percentageCellWidth;
9142 }
9143
9144 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9145 minColWidths[i] = cell->GetMinSize().x;
9146 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9147 maxColWidths[i] = cell->GetMaxSize().x;
9148 }
9149 }
9150 }
9151 }
9152
9153 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9154 // TODO: simply merge this into (1).
9155 for (i = 0; i < m_colCount; i++)
9156 {
9157 if (absoluteColWidths[i] > 0)
9158 {
9159 colWidths[i] = absoluteColWidths[i];
9160 }
9161 else if (percentageColWidths[i] > 0)
9162 {
9163 colWidths[i] = percentageColWidths[i];
9164
9165 // This is rubbish - we calculated the absolute widths from percentages, so
9166 // we can't do it again here.
9167 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9168 }
9169 }
9170
9171 // (3) Process absolute or proportional widths of spanning columns,
9172 // now that we know what our fixed column widths are going to be.
9173 // Spanned cells will try to adjust columns so the span will fit.
9174 // Even existing fixed column widths can be expanded if necessary.
9175 // Actually, currently fixed columns widths aren't adjusted; instead,
9176 // the algorithm favours earlier rows and adjusts unspecified column widths
9177 // the first time only. After that, we can't know whether the column has been
9178 // specified explicitly or not. (We could make a note if necessary.)
9179 for (j = 0; j < m_rowCount; j++)
9180 {
9181 // First get the overall margins so we can calculate percentage widths based on
9182 // the available content space for all cells on the row
9183
9184 int overallRowContentMargin = 0;
9185 int visibleCellCount = 0;
9186
9187 for (i = 0; i < m_colCount; i++)
9188 {
9189 wxRichTextBox* cell = GetCell(j, i);
9190 if (cell->IsShown())
9191 {
9192 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9193 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9194
9195 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9196 visibleCellCount ++;
9197 }
9198 }
9199
9200 // Add in inter-cell padding
9201 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9202
9203 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9204 wxSize rowTableSize(rowContentWidth, 0);
9205 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9206
9207 for (i = 0; i < m_colCount; i++)
9208 {
9209 wxRichTextBox* cell = GetCell(j, i);
9210 if (cell->IsShown())
9211 {
9212 int colSpan = 1;
9213 if (cell->GetProperties().HasProperty(wxT("colspan")))
9214 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9215
9216 if (colSpan > 1)
9217 {
9218 int spans = wxMin(colSpan, m_colCount - i);
9219 int cellWidth = 0;
9220 if (spans > 0)
9221 {
9222 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9223 {
9224 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9225 // Override absolute width with minimum width if necessary
9226 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9227 cellWidth = cell->GetMinSize().x;
9228 }
9229 else
9230 {
9231 // Do we want to do this? It's the only chance we get to
9232 // use the cell's min/max sizes, so we need to work out
9233 // how we're going to balance the unspecified spanning cell
9234 // width with the possibility more-constrained constituent cell widths.
9235 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9236 // don't want to constraint all the spanned columns to fit into this cell.
9237 // OK, let's say that if any of the constituent columns don't fit,
9238 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9239 // cells to the columns later.
9240 cellWidth = cell->GetMinSize().x;
9241 if (cell->GetMaxSize().x > cellWidth)
9242 cellWidth = cell->GetMaxSize().x;
9243 }
9244
9245 // Subtract the padding between cells
9246 int spanningWidth = cellWidth;
9247 spanningWidth -= paddingX * (spans-1);
9248
9249 if (spanningWidth > 0)
9250 {
9251 // Now share the spanning width between columns within that span
9252 // TODO: take into account min widths of columns within the span
9253 int spanningWidthLeft = spanningWidth;
9254 int stretchColCount = 0;
9255 for (k = i; k < (i+spans); k++)
9256 {
9257 if (colWidths[k] > 0) // absolute or proportional width has been specified
9258 spanningWidthLeft -= colWidths[k];
9259 else
9260 stretchColCount ++;
9261 }
9262 // Now divide what's left between the remaining columns
9263 int colShare = 0;
9264 if (stretchColCount > 0)
9265 colShare = spanningWidthLeft / stretchColCount;
9266 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9267
9268 // If fixed-width columns are currently too big, then we'll later
9269 // stretch the spanned cell to fit.
9270
9271 if (spanningWidthLeft > 0)
9272 {
9273 for (k = i; k < (i+spans); k++)
9274 {
9275 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9276 {
9277 int newWidth = colShare;
9278 if (k == (i+spans-1))
9279 newWidth += colShareRemainder; // ensure all pixels are filled
9280 colWidths[k] = newWidth;
9281 }
9282 }
9283 }
9284 }
9285 }
9286 }
9287 }
9288 }
9289 }
9290
9291 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9292 // TODO: take into account min widths of columns within the span
9293 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9294 int widthLeft = tableWidthMinusPadding;
9295 int stretchColCount = 0;
9296 for (i = 0; i < m_colCount; i++)
9297 {
9298 // TODO: we need to take into account min widths.
9299 // Subtract min width from width left, then
9300 // add the colShare to the min width
9301 if (colWidths[i] > 0) // absolute or proportional width has been specified
9302 widthLeft -= colWidths[i];
9303 else
9304 {
9305 if (minColWidths[i] > 0)
9306 widthLeft -= minColWidths[i];
9307
9308 stretchColCount ++;
9309 }
9310 }
9311
9312 // Now divide what's left between the remaining columns
9313 int colShare = 0;
9314 if (stretchColCount > 0)
9315 colShare = widthLeft / stretchColCount;
9316 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9317
9318 // Check we don't have enough space, in which case shrink all columns, overriding
9319 // any absolute/proportional widths
9320 // TODO: actually we would like to divide up the shrinkage according to size.
9321 // How do we calculate the proportions that will achieve this?
9322 // Could first choose an arbitrary value for stretching cells, and then calculate
9323 // factors to multiply each width by.
9324 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9325 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9326 {
9327 colShare = tableWidthMinusPadding / m_colCount;
9328 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9329 for (i = 0; i < m_colCount; i++)
9330 {
9331 colWidths[i] = 0;
9332 minColWidths[i] = 0;
9333 }
9334 }
9335
9336 // We have to adjust the columns if either we need to shrink the
9337 // table to fit the parent/table width, or we explicitly set the
9338 // table width and need to stretch out the table.
9339 if (widthLeft < 0 || stretchToFitTableWidth)
9340 {
9341 for (i = 0; i < m_colCount; i++)
9342 {
9343 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9344 {
9345 if (minColWidths[i] > 0)
9346 colWidths[i] = minColWidths[i] + colShare;
9347 else
9348 colWidths[i] = colShare;
9349 if (i == (m_colCount-1))
9350 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9351 }
9352 }
9353 }
9354
9355 // TODO: if spanned cells have no specified or max width, make them the
9356 // as big as the columns they span. Do this for all spanned cells in all
9357 // rows, of course. Size any spanned cells left over at the end - even if they
9358 // have width > 0, make sure they're limited to the appropriate column edge.
9359
9360
9361/*
9362 Sort out confusion between content width
9363 and overall width later. For now, assume we specify overall width.
9364
9365 So, now we've laid out the table to fit into the given space
9366 and have used specified widths and minimum widths.
9367
9368 Now we need to consider how we will try to take maximum width into account.
9369
9370*/
9371
9372 // (??) TODO: take max width into account
9373
9374 // (6) Lay out all cells again with the current values
9375
9376 int maxRight = 0;
9377 int y = availableSpace.y;
9378 for (j = 0; j < m_rowCount; j++)
9379 {
9380 int x = availableSpace.x; // TODO: take into account centering etc.
9381 int maxCellHeight = 0;
9382 int maxSpecifiedCellHeight = 0;
9383
bb7bbd12
JS
9384 wxArrayInt actualWidths;
9385 actualWidths.Add(0, m_colCount);
603f702b
JS
9386
9387 wxTextAttrDimensionConverter converter(dc, scale);
9388 for (i = 0; i < m_colCount; i++)
9389 {
9390 wxRichTextCell* cell = GetCell(j, i);
9391 if (cell->IsShown())
9392 {
603f702b
JS
9393 // Get max specified cell height
9394 // Don't handle percentages for height
9395 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9396 {
9397 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9398 if (h > maxSpecifiedCellHeight)
9399 maxSpecifiedCellHeight = h;
9400 }
9401
9402 if (colWidths[i] > 0) // absolute or proportional width has been specified
9403 {
9404 int colSpan = 1;
9405 if (cell->GetProperties().HasProperty(wxT("colspan")))
9406 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9407
9408 wxRect availableCellSpace;
9409
9410 // TODO: take into acount spans
9411 if (colSpan > 1)
9412 {
9413 // Calculate the size of this spanning cell from its constituent columns
9414 int xx = x;
9415 int spans = wxMin(colSpan, m_colCount - i);
9416 for (k = i; k < spans; k++)
9417 {
9418 if (k != i)
9419 xx += paddingX;
9420 xx += colWidths[k];
9421 }
9422 availableCellSpace = wxRect(x, y, xx, -1);
9423 }
9424 else
9425 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9426
9427 // Store actual width so we can force cell to be the appropriate width on the final loop
9428 actualWidths[i] = availableCellSpace.GetWidth();
9429
9430 // Lay out cell
9431 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9432 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9433
9434 // TODO: use GetCachedSize().x to compute 'natural' size
9435
9436 x += (availableCellSpace.GetWidth() + paddingX);
9437 if (cell->GetCachedSize().y > maxCellHeight)
9438 maxCellHeight = cell->GetCachedSize().y;
9439 }
9440 }
9441 }
9442
9443 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9444
9445 for (i = 0; i < m_colCount; i++)
9446 {
9447 wxRichTextCell* cell = GetCell(j, i);
9448 if (cell->IsShown())
9449 {
9450 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9451 // Lay out cell with new height
9452 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9453 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9454
9455 // Make sure the cell size really is the appropriate size,
9456 // not the calculated box size
9457 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9458
9459 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9460 }
9461 }
9462
9463 y += maxCellHeight;
9464 if (j < (m_rowCount-1))
9465 y += paddingY;
9466 }
9467
9468 // We need to add back the margins etc.
9469 {
9470 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9471 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 9472 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
9473 SetCachedSize(marginRect.GetSize());
9474 }
9475
9476 // TODO: calculate max size
9477 {
9478 SetMaxSize(GetCachedSize());
9479 }
9480
9481 // TODO: calculate min size
9482 {
9483 SetMinSize(GetCachedSize());
9484 }
9485
9486 // TODO: currently we use either a fixed table width or the parent's size.
9487 // We also want to be able to calculate the table width from its content,
9488 // whether using fixed column widths or cell content min/max width.
9489 // Probably need a boolean flag to say whether we need to stretch cells
9490 // to fit the table width, or to simply use min/max cell widths. The
9491 // trouble with this is that if cell widths are not specified, they
9492 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9493 // Anyway, ignoring that problem, we probably need to factor layout into a function
9494 // that can can calculate the maximum unconstrained layout in case table size is
9495 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9496 // constrain Layout(), or the previously-calculated max size to constraint layout.
9497
9498 return true;
9499}
9500
9501// Finds the absolute position and row height for the given character position
8db2e3ef 9502bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
9503{
9504 wxRichTextCell* child = GetCell(index+1);
9505 if (child)
9506 {
9507 // Find the position at the start of the child cell, since the table doesn't
9508 // have any caret position of its own.
8db2e3ef 9509 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
9510 }
9511 else
9512 return false;
9513}
9514
9515// Get the cell at the given character position (in the range of the table).
9516wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9517{
9518 int row = 0, col = 0;
9519 if (GetCellRowColumnPosition(pos, row, col))
9520 {
9521 return GetCell(row, col);
9522 }
9523 else
9524 return NULL;
9525}
9526
9527// Get the row/column for a given character position
9528bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9529{
9530 if (m_colCount == 0 || m_rowCount == 0)
9531 return false;
9532
9533 row = (int) (pos / m_colCount);
9534 col = pos - (row * m_colCount);
9535
9536 wxASSERT(row < m_rowCount && col < m_colCount);
9537
9538 if (row < m_rowCount && col < m_colCount)
9539 return true;
9540 else
9541 return false;
9542}
9543
9544// Calculate range, taking row/cell ordering into account instead of relying
9545// on list ordering.
9546void wxRichTextTable::CalculateRange(long start, long& end)
9547{
9548 long current = start;
9549 long lastEnd = current;
9550
9551 if (IsTopLevel())
9552 {
9553 current = 0;
9554 lastEnd = 0;
9555 }
9556
9557 int i, j;
9558 for (i = 0; i < m_rowCount; i++)
9559 {
9560 for (j = 0; j < m_colCount; j++)
9561 {
9562 wxRichTextCell* child = GetCell(i, j);
9563 if (child)
9564 {
9565 long childEnd = 0;
9566
9567 child->CalculateRange(current, childEnd);
9568
9569 lastEnd = childEnd;
9570 current = childEnd + 1;
9571 }
9572 }
9573 }
9574
9575 // A top-level object always has a range of size 1,
9576 // because its children don't count at this level.
9577 end = start;
9578 m_range.SetRange(start, start);
9579
9580 // An object with no children has zero length
9581 if (m_children.GetCount() == 0)
9582 lastEnd --;
9583 m_ownRange.SetRange(0, lastEnd);
9584}
9585
9586// Gets the range size.
8db2e3ef 9587bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b 9588{
8db2e3ef 9589 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
603f702b
JS
9590}
9591
9592// Deletes content in the given range.
9593bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
9594{
9595 // TODO: implement deletion of cells
9596 return true;
9597}
9598
9599// Gets any text in this object for the given range.
9600wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
9601{
9602 return wxRichTextBox::GetTextForRange(range);
9603}
9604
9605// Copies this object.
9606void wxRichTextTable::Copy(const wxRichTextTable& obj)
9607{
9608 wxRichTextBox::Copy(obj);
9609
9610 ClearTable();
9611
9612 m_rowCount = obj.m_rowCount;
9613 m_colCount = obj.m_colCount;
9614
9615 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
9616
9617 int i, j;
9618 for (i = 0; i < m_rowCount; i++)
9619 {
9620 wxRichTextObjectPtrArray& colArray = m_cells[i];
9621 for (j = 0; j < m_colCount; j++)
9622 {
9623 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
9624 AppendChild(cell);
9625
9626 colArray.Add(cell);
9627 }
9628 }
9629}
9630
9631void wxRichTextTable::ClearTable()
9632{
9633 m_cells.Clear();
9634 DeleteChildren();
9635}
9636
9637bool wxRichTextTable::CreateTable(int rows, int cols)
9638{
9639 ClearTable();
9640
9641 m_rowCount = rows;
9642 m_colCount = cols;
9643
9644 m_cells.Add(wxRichTextObjectPtrArray(), rows);
9645
9646 int i, j;
9647 for (i = 0; i < rows; i++)
9648 {
9649 wxRichTextObjectPtrArray& colArray = m_cells[i];
9650 for (j = 0; j < cols; j++)
9651 {
9652 wxRichTextCell* cell = new wxRichTextCell;
9653 AppendChild(cell);
9654 cell->AddParagraph(wxEmptyString);
9655
9656 colArray.Add(cell);
9657 }
9658 }
9659
9660 return true;
9661}
9662
9663wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
9664{
9665 wxASSERT(row < m_rowCount);
9666 wxASSERT(col < m_colCount);
9667
9668 if (row < m_rowCount && col < m_colCount)
9669 {
9670 wxRichTextObjectPtrArray& colArray = m_cells[row];
9671 wxRichTextObject* obj = colArray[col];
9672 return wxDynamicCast(obj, wxRichTextCell);
9673 }
9674 else
d67faa04 9675 return NULL;
603f702b
JS
9676}
9677
9678// Returns a selection object specifying the selections between start and end character positions.
9679// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9680wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
9681{
9682 wxRichTextSelection selection;
9683 selection.SetContainer((wxRichTextTable*) this);
9684
9685 if (start > end)
9686 {
9687 long tmp = end;
9688 end = start;
9689 start = tmp;
9690 }
9691
9692 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
9693
9694 if (end >= (m_colCount * m_rowCount))
9695 return selection;
9696
9697 // We need to find the rectangle of cells that is described by the rectangle
9698 // with start, end as the diagonal. Make sure we don't add cells that are
9699 // not currenty visible because they are overlapped by spanning cells.
9700/*
9701 --------------------------
9702 | 0 | 1 | 2 | 3 | 4 |
9703 --------------------------
9704 | 5 | 6 | 7 | 8 | 9 |
9705 --------------------------
9706 | 10 | 11 | 12 | 13 | 14 |
9707 --------------------------
9708 | 15 | 16 | 17 | 18 | 19 |
9709 --------------------------
9710
9711 Let's say we select 6 -> 18.
9712
9713 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9714 which is left and which is right.
9715
9716 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9717
9718 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9719 and (b) shown.
9720
9721
9722*/
9723
9724 int leftCol = start - m_colCount * int(start/m_colCount);
9725 int rightCol = end - m_colCount * int(end/m_colCount);
9726
9727 int topRow = int(start/m_colCount);
9728 int bottomRow = int(end/m_colCount);
9729
9730 if (leftCol > rightCol)
9731 {
9732 int tmp = rightCol;
9733 rightCol = leftCol;
9734 leftCol = tmp;
9735 }
9736
9737 if (topRow > bottomRow)
9738 {
9739 int tmp = bottomRow;
9740 bottomRow = topRow;
9741 topRow = tmp;
9742 }
9743
9744 int i, j;
9745 for (i = topRow; i <= bottomRow; i++)
9746 {
9747 for (j = leftCol; j <= rightCol; j++)
9748 {
9749 wxRichTextCell* cell = GetCell(i, j);
9750 if (cell && cell->IsShown())
9751 selection.Add(cell->GetRange());
9752 }
9753 }
9754
9755 return selection;
9756}
9757
9758// Sets the attributes for the cells specified by the selection.
9759bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
9760{
9761 if (selection.GetContainer() != this)
9762 return false;
9763
9764 wxRichTextBuffer* buffer = GetBuffer();
9765 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
9766 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
9767
9768 if (withUndo)
9769 buffer->BeginBatchUndo(_("Set Cell Style"));
9770
9771 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
9772 while (node)
9773 {
9774 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
9775 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
9776 SetStyle(cell, style, flags);
9777 node = node->GetNext();
9778 }
9779
9780 // Do action, or delay it until end of batch.
9781 if (withUndo)
9782 buffer->EndBatchUndo();
9783
9784 return true;
9785}
9786
9787bool wxRichTextTable::DeleteRows(int startRow, int noRows)
9788{
9789 wxASSERT((startRow + noRows) < m_rowCount);
9790 if ((startRow + noRows) >= m_rowCount)
9791 return false;
9792
9793 int i, j;
9794 for (i = startRow; i < (startRow+noRows); i++)
9795 {
9796 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
9797 for (j = 0; j < (int) colArray.GetCount(); j++)
9798 {
9799 wxRichTextObject* cell = colArray[j];
9800 RemoveChild(cell, true);
9801 }
9802
9803 // Keep deleting at the same position, since we move all
9804 // the others up
9805 m_cells.RemoveAt(startRow);
9806 }
9807
9808 m_rowCount = m_rowCount - noRows;
9809
9810 return true;
9811}
9812
9813bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
9814{
9815 wxASSERT((startCol + noCols) < m_colCount);
9816 if ((startCol + noCols) >= m_colCount)
9817 return false;
9818
9819 bool deleteRows = (noCols == m_colCount);
9820
9821 int i, j;
9822 for (i = 0; i < m_rowCount; i++)
9823 {
9824 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
9825 for (j = startCol; j < (startCol+noCols); j++)
9826 {
9827 wxRichTextObject* cell = colArray[j];
9828 RemoveChild(cell, true);
9829 }
9830
9831 if (deleteRows)
9832 m_cells.RemoveAt(0);
9833 }
9834
9835 if (deleteRows)
9836 m_rowCount = 0;
9837 m_colCount = m_colCount - noCols;
9838
9839 return true;
9840}
9841
9842bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
9843{
9844 wxASSERT(startRow <= m_rowCount);
9845 if (startRow > m_rowCount)
9846 return false;
9847
9848 int i, j;
9849 for (i = 0; i < noRows; i++)
9850 {
9851 int idx;
9852 if (startRow == m_rowCount)
9853 {
9854 m_cells.Add(wxRichTextObjectPtrArray());
9855 idx = m_cells.GetCount() - 1;
9856 }
9857 else
9858 {
9859 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
9860 idx = startRow+i;
9861 }
9862
9863 wxRichTextObjectPtrArray& colArray = m_cells[idx];
9864 for (j = 0; j < m_colCount; j++)
9865 {
9866 wxRichTextCell* cell = new wxRichTextCell;
9867 cell->GetAttributes() = attr;
9868
9869 AppendChild(cell);
9870 colArray.Add(cell);
9871 }
9872 }
9873
9874 m_rowCount = m_rowCount + noRows;
9875 return true;
9876}
9877
9878bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
9879{
9880 wxASSERT(startCol <= m_colCount);
9881 if (startCol > m_colCount)
9882 return false;
9883
9884 int i, j;
9885 for (i = 0; i < m_rowCount; i++)
9886 {
9887 wxRichTextObjectPtrArray& colArray = m_cells[i];
9888 for (j = 0; j < noCols; j++)
9889 {
9890 wxRichTextCell* cell = new wxRichTextCell;
9891 cell->GetAttributes() = attr;
9892
9893 AppendChild(cell);
9894
9895 if (startCol == m_colCount)
9896 colArray.Add(cell);
9897 else
9898 colArray.Insert(cell, startCol+j);
9899 }
9900 }
9901
9902 m_colCount = m_colCount + noCols;
9903
9904 return true;
5ad9ae3a
JS
9905}
9906
603f702b
JS
9907// Edit properties via a GUI
9908bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 9909{
603f702b
JS
9910 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
9911 boxDlg.SetAttributes(GetAttributes());
9912
9913 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 9914 {
603f702b
JS
9915 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9916 return true;
5ad9ae3a
JS
9917 }
9918 else
9919 return false;
bec80f4f
JS
9920}
9921
5d7836c4
JS
9922/*
9923 * Module to initialise and clean up handlers
9924 */
9925
9926class wxRichTextModule: public wxModule
9927{
9928DECLARE_DYNAMIC_CLASS(wxRichTextModule)
9929public:
9930 wxRichTextModule() {}
cfa3b256
JS
9931 bool OnInit()
9932 {
d2d0adc7 9933 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
9934 wxRichTextBuffer::InitStandardHandlers();
9935 wxRichTextParagraph::InitDefaultTabs();
1aca9fcd
JS
9936
9937 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9938 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9939 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9940 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9941 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9942 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9943 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9944 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9945 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9946
cfa3b256 9947 return true;
47b378bd 9948 }
cfa3b256
JS
9949 void OnExit()
9950 {
9951 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 9952 wxRichTextBuffer::CleanUpDrawingHandlers();
7c9fdebe 9953 wxRichTextBuffer::CleanUpFieldTypes();
1aca9fcd 9954 wxRichTextXMLHandler::ClearNodeToClassMap();
cfa3b256
JS
9955 wxRichTextDecimalToRoman(-1);
9956 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 9957 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 9958 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 9959 }
5d7836c4
JS
9960};
9961
9962IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
9963
9964
f1d6804f
RD
9965// If the richtext lib is dynamically loaded after the app has already started
9966// (such as from wxPython) then the built-in module system will not init this
9967// module. Provide this function to do it manually.
9968void wxRichTextModuleInit()
9969{
9970 wxModule* module = new wxRichTextModule;
9971 module->Init();
9972 wxModule::RegisterModule(module);
9973}
9974
9975
5d7836c4
JS
9976/*!
9977 * Commands for undo/redo
9978 *
9979 */
9980
9981wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 9982 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 9983{
603f702b 9984 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
9985}
9986
7fe8059f 9987wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
9988{
9989}
9990
9991wxRichTextCommand::~wxRichTextCommand()
9992{
9993 ClearActions();
9994}
9995
9996void wxRichTextCommand::AddAction(wxRichTextAction* action)
9997{
9998 if (!m_actions.Member(action))
9999 m_actions.Append(action);
10000}
10001
10002bool wxRichTextCommand::Do()
10003{
09f14108 10004 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
10005 {
10006 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10007 action->Do();
10008 }
10009
10010 return true;
10011}
10012
10013bool wxRichTextCommand::Undo()
10014{
09f14108 10015 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
10016 {
10017 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10018 action->Undo();
10019 }
10020
10021 return true;
10022}
10023
10024void wxRichTextCommand::ClearActions()
10025{
10026 WX_CLEAR_LIST(wxList, m_actions);
10027}
10028
10029/*!
10030 * Individual action
10031 *
10032 */
10033
603f702b
JS
10034wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10035 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10036 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
10037{
10038 m_buffer = buffer;
603f702b
JS
10039 m_object = NULL;
10040 m_containerAddress.Create(buffer, container);
5d7836c4
JS
10041 m_ignoreThis = ignoreFirstTime;
10042 m_cmdId = id;
10043 m_position = -1;
10044 m_ctrl = ctrl;
10045 m_name = name;
10046 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10047 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10048 if (cmd)
10049 cmd->AddAction(this);
10050}
10051
10052wxRichTextAction::~wxRichTextAction()
10053{
603f702b
JS
10054 if (m_object)
10055 delete m_object;
10056}
10057
10058// Returns the container that this action refers to, using the container address and top-level buffer.
10059wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10060{
10061 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10062 return container;
5d7836c4
JS
10063}
10064
603f702b 10065
7051fa41
JS
10066void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10067{
10068 // Store a list of line start character and y positions so we can figure out which area
10069 // we need to refresh
10070
10071#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
10072 wxRichTextParagraphLayoutBox* container = GetContainer();
10073 wxASSERT(container != NULL);
10074 if (!container)
10075 return;
10076
7051fa41
JS
10077 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10078 // If we had several actions, which only invalidate and leave layout until the
10079 // paint handler is called, then this might not be true. So we may need to switch
10080 // optimisation on only when we're simply adding text and not simultaneously
10081 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10082 // first, but of course this means we'll be doing it twice.
603f702b 10083 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41 10084 {
4ba36292
JS
10085 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10086 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
7051fa41
JS
10087 int lastY = firstVisiblePt.y + clientSize.y;
10088
603f702b
JS
10089 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10090 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
10091 while (node)
10092 {
10093 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10094 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10095 while (node2)
10096 {
10097 wxRichTextLine* line = node2->GetData();
10098 wxPoint pt = line->GetAbsolutePosition();
10099 wxRichTextRange range = line->GetAbsoluteRange();
10100
10101 if (pt.y > lastY)
10102 {
10103 node2 = wxRichTextLineList::compatibility_iterator();
10104 node = wxRichTextObjectList::compatibility_iterator();
10105 }
10106 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10107 {
10108 optimizationLineCharPositions.Add(range.GetStart());
10109 optimizationLineYPositions.Add(pt.y);
10110 }
10111
10112 if (node2)
10113 node2 = node2->GetNext();
10114 }
10115
10116 if (node)
10117 node = node->GetNext();
10118 }
10119 }
10120#endif
10121}
10122
5d7836c4
JS
10123bool wxRichTextAction::Do()
10124{
10125 m_buffer->Modify(true);
10126
603f702b
JS
10127 wxRichTextParagraphLayoutBox* container = GetContainer();
10128 wxASSERT(container != NULL);
10129 if (!container)
10130 return false;
10131
5d7836c4
JS
10132 switch (m_cmdId)
10133 {
10134 case wxRICHTEXT_INSERT:
10135 {
ea160b2e
JS
10136 // Store a list of line start character and y positions so we can figure out which area
10137 // we need to refresh
10138 wxArrayInt optimizationLineCharPositions;
10139 wxArrayInt optimizationLineYPositions;
10140
10141#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 10142 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
10143#endif
10144
603f702b
JS
10145 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10146 container->UpdateRanges();
10147
10148 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10149 // Layout() would stop prematurely at the top level.
10150 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 10151
603f702b 10152 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
10153
10154 // Character position to caret position
10155 newCaretPosition --;
10156
10157 // Don't take into account the last newline
5d7836c4
JS
10158 if (m_newParagraphs.GetPartialParagraph())
10159 newCaretPosition --;
46ee0e5b 10160 else
7c081bd2 10161 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
10162 {
10163 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10164 if (p->GetRange().GetLength() == 1)
10165 newCaretPosition --;
10166 }
5d7836c4 10167
603f702b 10168 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 10169
7051fa41 10170 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 10171
5912d19e
JS
10172 wxRichTextEvent cmdEvent(
10173 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10174 m_ctrl ? m_ctrl->GetId() : -1);
10175 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10176 cmdEvent.SetRange(GetRange());
10177 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10178 cmdEvent.SetContainer(container);
3e541562 10179
5912d19e 10180 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
10181
10182 break;
10183 }
10184 case wxRICHTEXT_DELETE:
10185 {
7051fa41
JS
10186 wxArrayInt optimizationLineCharPositions;
10187 wxArrayInt optimizationLineYPositions;
10188
10189#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10190 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10191#endif
10192
603f702b
JS
10193 container->DeleteRange(GetRange());
10194 container->UpdateRanges();
10195 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10196 // Layout() would stop prematurely at the top level.
10197 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 10198
6ccbca24 10199 long caretPos = GetRange().GetStart()-1;
603f702b 10200 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
10201 caretPos --;
10202
7051fa41 10203 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 10204
5912d19e
JS
10205 wxRichTextEvent cmdEvent(
10206 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10207 m_ctrl ? m_ctrl->GetId() : -1);
10208 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10209 cmdEvent.SetRange(GetRange());
10210 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10211 cmdEvent.SetContainer(container);
3e541562 10212
5912d19e
JS
10213 m_buffer->SendEvent(cmdEvent);
10214
5d7836c4
JS
10215 break;
10216 }
10217 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10218 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10219 {
10220 ApplyParagraphs(GetNewParagraphs());
603f702b
JS
10221
10222 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10223 // Layout() would stop prematurely at the top level.
10224 container->InvalidateHierarchy(GetRange());
10225
10226 UpdateAppearance(GetPosition());
10227
10228 wxRichTextEvent cmdEvent(
590a0f8b 10229 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
10230 m_ctrl ? m_ctrl->GetId() : -1);
10231 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10232 cmdEvent.SetRange(GetRange());
10233 cmdEvent.SetPosition(GetRange().GetStart());
10234 cmdEvent.SetContainer(container);
10235
10236 m_buffer->SendEvent(cmdEvent);
10237
10238 break;
10239 }
10240 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10241 {
10242 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10243 if (obj)
10244 {
10245 wxRichTextAttr oldAttr = obj->GetAttributes();
10246 obj->GetAttributes() = m_attributes;
10247 m_attributes = oldAttr;
10248 }
10249
10250 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10251 // Layout() would stop prematurely at the top level.
10252 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10253
10254 UpdateAppearance(GetPosition());
10255
5912d19e
JS
10256 wxRichTextEvent cmdEvent(
10257 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10258 m_ctrl ? m_ctrl->GetId() : -1);
10259 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10260 cmdEvent.SetRange(GetRange());
10261 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10262 cmdEvent.SetContainer(container);
3e541562 10263
5912d19e
JS
10264 m_buffer->SendEvent(cmdEvent);
10265
603f702b
JS
10266 break;
10267 }
10268 case wxRICHTEXT_CHANGE_OBJECT:
10269 {
10270 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10271 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10272 if (obj && m_object)
10273 {
10274 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10275 if (node)
10276 {
10277 wxRichTextObject* obj = node->GetData();
10278 node->SetData(m_object);
10279 m_object = obj;
10280 }
10281 }
10282
10283 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10284 // Layout() would stop prematurely at the top level.
10285 container->InvalidateHierarchy(GetRange());
10286
10287 UpdateAppearance(GetPosition());
10288
10289 // TODO: send new kind of modification event
10290
5d7836c4
JS
10291 break;
10292 }
10293 default:
10294 break;
10295 }
10296
10297 return true;
10298}
10299
10300bool wxRichTextAction::Undo()
10301{
10302 m_buffer->Modify(true);
10303
603f702b
JS
10304 wxRichTextParagraphLayoutBox* container = GetContainer();
10305 wxASSERT(container != NULL);
10306 if (!container)
10307 return false;
10308
5d7836c4
JS
10309 switch (m_cmdId)
10310 {
10311 case wxRICHTEXT_INSERT:
10312 {
7051fa41
JS
10313 wxArrayInt optimizationLineCharPositions;
10314 wxArrayInt optimizationLineYPositions;
10315
10316#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10317 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10318#endif
10319
603f702b
JS
10320 container->DeleteRange(GetRange());
10321 container->UpdateRanges();
7c9fdebe 10322
603f702b
JS
10323 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10324 // Layout() would stop prematurely at the top level.
10325 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
10326
10327 long newCaretPosition = GetPosition() - 1;
3e541562 10328
7051fa41 10329 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10330
5912d19e
JS
10331 wxRichTextEvent cmdEvent(
10332 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10333 m_ctrl ? m_ctrl->GetId() : -1);
10334 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10335 cmdEvent.SetRange(GetRange());
10336 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10337 cmdEvent.SetContainer(container);
3e541562 10338
5912d19e
JS
10339 m_buffer->SendEvent(cmdEvent);
10340
5d7836c4
JS
10341 break;
10342 }
10343 case wxRICHTEXT_DELETE:
10344 {
7051fa41
JS
10345 wxArrayInt optimizationLineCharPositions;
10346 wxArrayInt optimizationLineYPositions;
10347
10348#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10349 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10350#endif
10351
603f702b
JS
10352 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10353 container->UpdateRanges();
7c9fdebe 10354
603f702b
JS
10355 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10356 // Layout() would stop prematurely at the top level.
10357 container->InvalidateHierarchy(GetRange());
5d7836c4 10358
7051fa41 10359 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10360
5912d19e
JS
10361 wxRichTextEvent cmdEvent(
10362 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10363 m_ctrl ? m_ctrl->GetId() : -1);
10364 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10365 cmdEvent.SetRange(GetRange());
10366 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10367 cmdEvent.SetContainer(container);
3e541562 10368
5912d19e
JS
10369 m_buffer->SendEvent(cmdEvent);
10370
5d7836c4
JS
10371 break;
10372 }
10373 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10374 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10375 {
10376 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
10377 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10378 // Layout() would stop prematurely at the top level.
10379 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10380
10381 UpdateAppearance(GetPosition());
10382
5912d19e 10383 wxRichTextEvent cmdEvent(
590a0f8b 10384 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
10385 m_ctrl ? m_ctrl->GetId() : -1);
10386 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10387 cmdEvent.SetRange(GetRange());
10388 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10389 cmdEvent.SetContainer(container);
3e541562 10390
5912d19e
JS
10391 m_buffer->SendEvent(cmdEvent);
10392
5d7836c4
JS
10393 break;
10394 }
603f702b
JS
10395 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10396 case wxRICHTEXT_CHANGE_OBJECT:
10397 {
10398 return Do();
10399 }
5d7836c4
JS
10400 default:
10401 break;
10402 }
10403
10404 return true;
10405}
10406
10407/// Update the control appearance
603f702b 10408void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 10409{
603f702b
JS
10410 wxRichTextParagraphLayoutBox* container = GetContainer();
10411 wxASSERT(container != NULL);
10412 if (!container)
10413 return;
10414
5d7836c4
JS
10415 if (m_ctrl)
10416 {
603f702b 10417 m_ctrl->SetFocusObject(container);
5d7836c4 10418 m_ctrl->SetCaretPosition(caretPosition);
603f702b 10419
5d7836c4
JS
10420 if (!m_ctrl->IsFrozen())
10421 {
603f702b
JS
10422 wxRect containerRect = container->GetRect();
10423
2f36e8dc 10424 m_ctrl->LayoutContent();
5d7836c4 10425
603f702b
JS
10426 // Refresh everything if there were floating objects or the container changed size
10427 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10428 if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
10429 {
10430 m_ctrl->Refresh(false);
10431 }
10432 else
10433
10434#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10435 // Find refresh rectangle if we are in a position to optimise refresh
10436 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10437 {
10438 size_t i;
10439
4ba36292
JS
10440 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10441 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
603f702b
JS
10442
10443 // Start/end positions
10444 int firstY = 0;
10445 int lastY = firstVisiblePt.y + clientSize.y;
10446
10447 bool foundEnd = false;
10448
10449 // position offset - how many characters were inserted
10450 int positionOffset = GetRange().GetLength();
10451
10452 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10453 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10454 positionOffset = - positionOffset;
10455
10456 // find the first line which is being drawn at the same position as it was
10457 // before. Since we're talking about a simple insertion, we can assume
10458 // that the rest of the window does not need to be redrawn.
10459
10460 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10461 // Since we support floating layout, we should redraw the whole para instead of just
10462 // the first line touching the invalid range.
10463 if (para)
10464 {
10465 firstY = para->GetPosition().y;
10466 }
10467
10468 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10469 while (node)
10470 {
10471 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10472 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10473 while (node2)
10474 {
10475 wxRichTextLine* line = node2->GetData();
10476 wxPoint pt = line->GetAbsolutePosition();
10477 wxRichTextRange range = line->GetAbsoluteRange();
10478
10479 // we want to find the first line that is in the same position
10480 // as before. This will mean we're at the end of the changed text.
10481
10482 if (pt.y > lastY) // going past the end of the window, no more info
10483 {
10484 node2 = wxRichTextLineList::compatibility_iterator();
10485 node = wxRichTextObjectList::compatibility_iterator();
10486 }
10487 // Detect last line in the buffer
10488 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10489 {
10490 // If deleting text, make sure we refresh below as well as above
10491 if (positionOffset >= 0)
10492 {
10493 foundEnd = true;
10494 lastY = pt.y + line->GetSize().y;
10495 }
10496
10497 node2 = wxRichTextLineList::compatibility_iterator();
10498 node = wxRichTextObjectList::compatibility_iterator();
10499
10500 break;
10501 }
10502 else
10503 {
10504 // search for this line being at the same position as before
10505 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10506 {
10507 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10508 ((*optimizationLineYPositions)[i] == pt.y))
10509 {
10510 // Stop, we're now the same as we were
10511 foundEnd = true;
10512
10513 lastY = pt.y;
10514
10515 node2 = wxRichTextLineList::compatibility_iterator();
10516 node = wxRichTextObjectList::compatibility_iterator();
10517
10518 break;
10519 }
10520 }
10521 }
10522
10523 if (node2)
10524 node2 = node2->GetNext();
10525 }
10526
10527 if (node)
10528 node = node->GetNext();
10529 }
10530
10531 firstY = wxMax(firstVisiblePt.y, firstY);
10532 if (!foundEnd)
10533 lastY = firstVisiblePt.y + clientSize.y;
10534
10535 // Convert to device coordinates
4ba36292 10536 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
603f702b
JS
10537 m_ctrl->RefreshRect(rect);
10538 }
10539 else
1c13f06e 10540#endif
603f702b
JS
10541 m_ctrl->Refresh(false);
10542
10543 m_ctrl->PositionCaret();
4fe83b93
JS
10544
10545 // This causes styles to persist when doing programmatic
10546 // content creation except when Freeze/Thaw is used, so
10547 // disable this and check for the consequences.
10548 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 10549
5d7836c4 10550 if (sendUpdateEvent)
0ec1179b 10551 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 10552 }
7fe8059f 10553 }
5d7836c4
JS
10554}
10555
10556/// Replace the buffer paragraphs with the new ones.
0ca07313 10557void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 10558{
603f702b
JS
10559 wxRichTextParagraphLayoutBox* container = GetContainer();
10560 wxASSERT(container != NULL);
10561 if (!container)
10562 return;
10563
5d7836c4
JS
10564 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
10565 while (node)
10566 {
10567 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
10568 wxASSERT (para != NULL);
10569
10570 // We'll replace the existing paragraph by finding the paragraph at this position,
10571 // delete its node data, and setting a copy as the new node data.
10572 // TODO: make more efficient by simply swapping old and new paragraph objects.
10573
603f702b 10574 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
10575 if (existingPara)
10576 {
603f702b 10577 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
10578 if (bufferParaNode)
10579 {
10580 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 10581 newPara->SetParent(container);
5d7836c4
JS
10582
10583 bufferParaNode->SetData(newPara);
10584
10585 delete existingPara;
10586 }
10587 }
10588
10589 node = node->GetNext();
10590 }
10591}
10592
10593
10594/*!
10595 * wxRichTextRange
10596 * This stores beginning and end positions for a range of data.
10597 */
10598
603f702b
JS
10599WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
10600
5d7836c4
JS
10601/// Limit this range to be within 'range'
10602bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
10603{
10604 if (m_start < range.m_start)
10605 m_start = range.m_start;
10606
10607 if (m_end > range.m_end)
10608 m_end = range.m_end;
10609
10610 return true;
10611}
10612
10613/*!
10614 * wxRichTextImage implementation
10615 * This object represents an image.
10616 */
10617
bec80f4f 10618IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 10619
24777478 10620wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 10621 wxRichTextObject(parent)
5d7836c4 10622{
23698b12 10623 Init();
cdaed652 10624 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
10625 if (charStyle)
10626 SetAttributes(*charStyle);
5d7836c4
JS
10627}
10628
24777478 10629wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 10630 wxRichTextObject(parent)
5d7836c4 10631{
23698b12 10632 Init();
5d7836c4 10633 m_imageBlock = imageBlock;
4f32b3cf
JS
10634 if (charStyle)
10635 SetAttributes(*charStyle);
5d7836c4
JS
10636}
10637
23698b12
JS
10638void wxRichTextImage::Init()
10639{
10640 m_originalImageSize = wxSize(-1, -1);
10641}
10642
cdaed652
VZ
10643/// Create a cached image at the required size
10644bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
5d7836c4 10645{
23698b12
JS
10646 if (!m_imageBlock.IsOk())
10647 return false;
10648
10649 // If we have an original image size, use that to compute the cached bitmap size
10650 // instead of loading the image each time. This way we can avoid loading
10651 // the image so long as the new cached bitmap size hasn't changed.
10652
10653 wxImage image;
10654 if (resetCache || m_originalImageSize == wxSize(-1, -1))
cdaed652 10655 {
23698b12 10656 m_imageCache = wxNullBitmap;
ce00f59b 10657
cdaed652
VZ
10658 m_imageBlock.Load(image);
10659 if (!image.IsOk())
10660 return false;
ce00f59b 10661
23698b12
JS
10662 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
10663 }
10664
10665 int width = m_originalImageSize.GetWidth();
10666 int height = m_originalImageSize.GetHeight();
10667
10668 int parentWidth = 0;
10669 int parentHeight = 0;
bec80f4f 10670
23698b12
JS
10671 int maxWidth = -1;
10672 int maxHeight = -1;
10673
10674 wxRichTextBuffer* buffer = GetBuffer();
10675 if (buffer)
10676 {
10677 wxSize sz;
10678 if (buffer->GetRichTextCtrl())
cdaed652 10679 {
23698b12
JS
10680 // Subtract borders
10681 sz = buffer->GetRichTextCtrl()->GetClientSize();
10682
10683 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10684 marginRect = wxRect(0, 0, sz.x, sz.y);
10685 buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
10686
10687 sz = contentRect.GetSize();
10688
10689 // Start with a maximum width of the control size, even if not specified by the content,
10690 // to minimize the amount of picture overlapping the right-hand side
10691 maxWidth = sz.x;
cdaed652 10692 }
23698b12
JS
10693 else
10694 sz = buffer->GetCachedSize();
10695 parentWidth = sz.GetWidth();
10696 parentHeight = sz.GetHeight();
10697 }
10698
10699 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10700 {
10701 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10702 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
10703 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10704 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10705 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10706 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10707 }
10708
10709 // Limit to max width
10710
10711 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10712 {
10713 int mw = -1;
10714
10715 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10716 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
10717 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10718 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10719 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10720 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10721
10722 // If we already have a smaller max width due to the constraints of the control size,
10723 // don't use the larger max width.
10724 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
10725 maxWidth = mw;
10726 }
10727
10728 if (maxWidth > 0 && width > maxWidth)
10729 width = maxWidth;
10730
10731 // Preserve the aspect ratio
10732 if (width != m_originalImageSize.GetWidth())
10733 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
10734
10735 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10736 {
10737 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10738 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
10739 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10740 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10741 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10742 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10743
10744 // Preserve the aspect ratio
10745 if (height != m_originalImageSize.GetHeight())
10746 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
10747 }
10748
10749 // Limit to max height
10750
10751 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10752 {
10753 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10754 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
10755 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10756 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10757 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10758 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10759 }
10760
10761 if (maxHeight > 0 && height > maxHeight)
10762 {
10763 height = maxHeight;
10764
10765 // Preserve the aspect ratio
10766 if (height != m_originalImageSize.GetHeight())
10767 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
10768 }
10769
10770 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
10771 {
10772 // Do nothing, we didn't need to change the image cache
10773 }
10774 else
10775 {
10776 if (!image.IsOk())
cdaed652 10777 {
23698b12
JS
10778 m_imageBlock.Load(image);
10779 if (!image.IsOk())
10780 return false;
cdaed652 10781 }
5d7836c4 10782
cdaed652
VZ
10783 if (image.GetWidth() == width && image.GetHeight() == height)
10784 m_imageCache = wxBitmap(image);
10785 else
10786 {
10787 // If the original width and height is small, e.g. 400 or below,
10788 // scale up and then down to improve image quality. This can make
10789 // a big difference, with not much performance hit.
10790 int upscaleThreshold = 400;
10791 wxImage img;
10792 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
10793 {
10794 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
10795 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
10796 }
10797 else
10798 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
10799 m_imageCache = wxBitmap(img);
10800 }
10801 }
ce00f59b 10802
cdaed652 10803 return m_imageCache.IsOk();
5d7836c4
JS
10804}
10805
5d7836c4 10806/// Draw the item
20d09da5 10807bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 10808{
603f702b
JS
10809 if (!IsShown())
10810 return true;
10811
cdaed652
VZ
10812 // Don't need cached size AFAIK
10813 // wxSize size = GetCachedSize();
10814 if (!LoadImageCache(dc))
5d7836c4 10815 return false;
ce00f59b 10816
8db2e3ef
JS
10817 wxRichTextAttr attr(GetAttributes());
10818 context.ApplyVirtualAttributes(attr, this);
10819
10820 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b 10821
603f702b
JS
10822 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10823 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10824 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 10825 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10826
10827 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 10828
a70eb13e 10829 if (selection.WithinSelection(GetRange().GetStart(), this))
5d7836c4 10830 {
ecb5fbf1
JS
10831 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
10832 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 10833 dc.SetLogicalFunction(wxINVERT);
603f702b 10834 dc.DrawRectangle(contentRect);
5d7836c4
JS
10835 dc.SetLogicalFunction(wxCOPY);
10836 }
10837
10838 return true;
10839}
10840
10841/// Lay the item out
8db2e3ef 10842bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 10843{
cdaed652
VZ
10844 if (!LoadImageCache(dc))
10845 return false;
5d7836c4 10846
603f702b
JS
10847 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10848 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10849 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
10850
10851 wxRichTextAttr attr(GetAttributes());
10852 context.ApplyVirtualAttributes(attr, this);
10853
10854 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10855
10856 wxSize overallSize = marginRect.GetSize();
10857
10858 SetCachedSize(overallSize);
10859 SetMaxSize(overallSize);
10860 SetMinSize(overallSize);
cdaed652 10861 SetPosition(rect.GetPosition());
5d7836c4
JS
10862
10863 return true;
10864}
10865
10866/// Get/set the object size for the given range. Returns false if the range
10867/// is invalid for this object.
8db2e3ef 10868bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
5d7836c4
JS
10869{
10870 if (!range.IsWithin(GetRange()))
10871 return false;
10872
cdaed652 10873 if (!((wxRichTextImage*)this)->LoadImageCache(dc))
31778480 10874 {
cdaed652
VZ
10875 size.x = 0; size.y = 0;
10876 if (partialExtents)
31778480 10877 partialExtents->Add(0);
cdaed652 10878 return false;
31778480 10879 }
ce00f59b 10880
8db2e3ef
JS
10881 wxRichTextAttr attr(GetAttributes());
10882 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
10883
603f702b
JS
10884 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10885 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10886 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef 10887 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10888
10889 wxSize overallSize = marginRect.GetSize();
31778480 10890
cdaed652 10891 if (partialExtents)
603f702b 10892 partialExtents->Add(overallSize.x);
5d7836c4 10893
603f702b 10894 size = overallSize;
5d7836c4
JS
10895
10896 return true;
10897}
10898
603f702b
JS
10899// Get the 'natural' size for an object. For an image, it would be the
10900// image size.
10901wxTextAttrSize wxRichTextImage::GetNaturalSize() const
10902{
10903 wxTextAttrSize size;
10904 if (GetImageCache().IsOk())
10905 {
10906 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
10907 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
10908 }
10909 return size;
10910}
10911
10912
5d7836c4
JS
10913/// Copy
10914void wxRichTextImage::Copy(const wxRichTextImage& obj)
10915{
bec80f4f 10916 wxRichTextObject::Copy(obj);
59509217 10917
5d7836c4 10918 m_imageBlock = obj.m_imageBlock;
23698b12 10919 m_originalImageSize = obj.m_originalImageSize;
5d7836c4
JS
10920}
10921
cdaed652
VZ
10922/// Edit properties via a GUI
10923bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10924{
603f702b
JS
10925 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
10926 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
10927
10928 if (imageDlg.ShowModal() == wxID_OK)
10929 {
603f702b
JS
10930 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10931 // indeterminate in the object.
10932 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
10933 return true;
10934 }
10935 else
10936 return false;
10937}
10938
5d7836c4
JS
10939/*!
10940 * Utilities
10941 *
10942 */
10943
10944/// Compare two attribute objects
24777478 10945bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 10946{
38f833b1 10947 return (attr1 == attr2);
5d7836c4
JS
10948}
10949
44cc96a8
JS
10950/// Compare tabs
10951bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
10952{
10953 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
10954 return false;
10955
44cc96a8
JS
10956 size_t i;
10957 for (i = 0; i < tabs1.GetCount(); i++)
10958 {
10959 if (tabs1[i] != tabs2[i])
10960 return false;
10961 }
10962 return true;
10963}
5d7836c4 10964
24777478 10965bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
10966{
10967 return destStyle.Apply(style, compareWith);
10968}
5d7836c4 10969
44cc96a8 10970// Remove attributes
24777478 10971bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 10972{
24777478 10973 return destStyle.RemoveStyle(style);
44cc96a8 10974}
5d7836c4 10975
44cc96a8
JS
10976/// Combine two bitlists, specifying the bits of interest with separate flags.
10977bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
10978{
24777478 10979 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 10980}
5d7836c4 10981
44cc96a8
JS
10982/// Compare two bitlists
10983bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
10984{
24777478 10985 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 10986}
38f833b1 10987
44cc96a8 10988/// Split into paragraph and character styles
24777478 10989bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 10990{
24777478 10991 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 10992}
5d7836c4 10993
44cc96a8
JS
10994/// Convert a decimal to Roman numerals
10995wxString wxRichTextDecimalToRoman(long n)
10996{
10997 static wxArrayInt decimalNumbers;
10998 static wxArrayString romanNumbers;
5d7836c4 10999
44cc96a8
JS
11000 // Clean up arrays
11001 if (n == -1)
11002 {
11003 decimalNumbers.Clear();
11004 romanNumbers.Clear();
11005 return wxEmptyString;
11006 }
5d7836c4 11007
44cc96a8
JS
11008 if (decimalNumbers.GetCount() == 0)
11009 {
11010 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 11011
44cc96a8
JS
11012 wxRichTextAddDecRom(1000, wxT("M"));
11013 wxRichTextAddDecRom(900, wxT("CM"));
11014 wxRichTextAddDecRom(500, wxT("D"));
11015 wxRichTextAddDecRom(400, wxT("CD"));
11016 wxRichTextAddDecRom(100, wxT("C"));
11017 wxRichTextAddDecRom(90, wxT("XC"));
11018 wxRichTextAddDecRom(50, wxT("L"));
11019 wxRichTextAddDecRom(40, wxT("XL"));
11020 wxRichTextAddDecRom(10, wxT("X"));
11021 wxRichTextAddDecRom(9, wxT("IX"));
11022 wxRichTextAddDecRom(5, wxT("V"));
11023 wxRichTextAddDecRom(4, wxT("IV"));
11024 wxRichTextAddDecRom(1, wxT("I"));
11025 }
5d7836c4 11026
44cc96a8
JS
11027 int i = 0;
11028 wxString roman;
ea160b2e 11029
44cc96a8 11030 while (n > 0 && i < 13)
42688aea 11031 {
44cc96a8
JS
11032 if (n >= decimalNumbers[i])
11033 {
11034 n -= decimalNumbers[i];
11035 roman += romanNumbers[i];
11036 }
11037 else
11038 {
11039 i ++;
11040 }
42688aea 11041 }
44cc96a8
JS
11042 if (roman.IsEmpty())
11043 roman = wxT("0");
11044 return roman;
11045}
42688aea 11046
44cc96a8
JS
11047/*!
11048 * wxRichTextFileHandler
11049 * Base class for file handlers
11050 */
4d6d8bf4 11051
44cc96a8 11052IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 11053
44cc96a8
JS
11054#if wxUSE_FFILE && wxUSE_STREAMS
11055bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 11056{
44cc96a8 11057 wxFFileInputStream stream(filename);
a1b806b9 11058 if (stream.IsOk())
44cc96a8 11059 return LoadFile(buffer, stream);
5d7836c4 11060
44cc96a8
JS
11061 return false;
11062}
5d7836c4 11063
44cc96a8
JS
11064bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11065{
11066 wxFFileOutputStream stream(filename);
a1b806b9 11067 if (stream.IsOk())
44cc96a8 11068 return SaveFile(buffer, stream);
5d7836c4 11069
44cc96a8
JS
11070 return false;
11071}
11072#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 11073
44cc96a8
JS
11074/// Can we handle this filename (if using files)? By default, checks the extension.
11075bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11076{
11077 wxString path, file, ext;
a51e601e 11078 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 11079
44cc96a8
JS
11080 return (ext.Lower() == GetExtension());
11081}
5d7836c4 11082
44cc96a8
JS
11083/*!
11084 * wxRichTextTextHandler
11085 * Plain text handler
11086 */
5d7836c4 11087
44cc96a8 11088IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 11089
44cc96a8
JS
11090#if wxUSE_STREAMS
11091bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11092{
11093 if (!stream.IsOk())
797e38dd
JS
11094 return false;
11095
44cc96a8
JS
11096 wxString str;
11097 int lastCh = 0;
5d7836c4 11098
44cc96a8
JS
11099 while (!stream.Eof())
11100 {
11101 int ch = stream.GetC();
5d7836c4 11102
44cc96a8
JS
11103 if (!stream.Eof())
11104 {
11105 if (ch == 10 && lastCh != 13)
11106 str += wxT('\n');
5d7836c4 11107
44cc96a8
JS
11108 if (ch > 0 && ch != 10)
11109 str += wxChar(ch);
5d7836c4 11110
44cc96a8
JS
11111 lastCh = ch;
11112 }
11113 }
5d7836c4 11114
44cc96a8
JS
11115 buffer->ResetAndClearCommands();
11116 buffer->Clear();
11117 buffer->AddParagraphs(str);
11118 buffer->UpdateRanges();
5d7836c4 11119
44cc96a8
JS
11120 return true;
11121}
5d7836c4 11122
44cc96a8
JS
11123bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11124{
11125 if (!stream.IsOk())
5d7836c4
JS
11126 return false;
11127
44cc96a8 11128 wxString text = buffer->GetText();
38f833b1 11129
44cc96a8
JS
11130 wxString newLine = wxRichTextLineBreakChar;
11131 text.Replace(newLine, wxT("\n"));
5d7836c4 11132
44cc96a8 11133 wxCharBuffer buf = text.ToAscii();
5d7836c4 11134
44cc96a8
JS
11135 stream.Write((const char*) buf, text.length());
11136 return true;
11137}
11138#endif // wxUSE_STREAMS
5d7836c4 11139
44cc96a8
JS
11140/*
11141 * Stores information about an image, in binary in-memory form
11142 */
59509217 11143
44cc96a8
JS
11144wxRichTextImageBlock::wxRichTextImageBlock()
11145{
11146 Init();
11147}
5d7836c4 11148
44cc96a8
JS
11149wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11150{
11151 Init();
11152 Copy(block);
11153}
ea160b2e 11154
44cc96a8
JS
11155wxRichTextImageBlock::~wxRichTextImageBlock()
11156{
5276b0a5 11157 wxDELETEA(m_data);
5d7836c4
JS
11158}
11159
44cc96a8 11160void wxRichTextImageBlock::Init()
5d7836c4
JS
11161{
11162 m_data = NULL;
11163 m_dataSize = 0;
d75a69e8 11164 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11165}
11166
11167void wxRichTextImageBlock::Clear()
11168{
5276b0a5 11169 wxDELETEA(m_data);
5d7836c4 11170 m_dataSize = 0;
d75a69e8 11171 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11172}
11173
11174
11175// Load the original image into a memory block.
11176// If the image is not a JPEG, we must convert it into a JPEG
11177// to conserve space.
11178// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11179// load the image a 2nd time.
11180
d75a69e8
FM
11181bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11182 wxImage& image, bool convertToJPEG)
5d7836c4
JS
11183{
11184 m_imageType = imageType;
11185
11186 wxString filenameToRead(filename);
7fe8059f 11187 bool removeFile = false;
5d7836c4 11188
62891c87 11189 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11190 return false; // Could not determine image type
5d7836c4
JS
11191
11192 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11193 {
a51e601e
FM
11194 wxString tempFile =
11195 wxFileName::CreateTempFileName(_("image"));
5d7836c4 11196
a51e601e 11197 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11198
11199 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11200 filenameToRead = tempFile;
7fe8059f 11201 removeFile = true;
5d7836c4
JS
11202
11203 m_imageType = wxBITMAP_TYPE_JPEG;
11204 }
11205 wxFile file;
11206 if (!file.Open(filenameToRead))
7fe8059f 11207 return false;
5d7836c4
JS
11208
11209 m_dataSize = (size_t) file.Length();
11210 file.Close();
11211
11212 if (m_data)
11213 delete[] m_data;
11214 m_data = ReadBlock(filenameToRead, m_dataSize);
11215
11216 if (removeFile)
11217 wxRemoveFile(filenameToRead);
11218
11219 return (m_data != NULL);
11220}
11221
11222// Make an image block from the wxImage in the given
11223// format.
d75a69e8 11224bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 11225{
5d7836c4
JS
11226 image.SetOption(wxT("quality"), quality);
11227
62891c87 11228 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11229 return false; // Could not determine image type
5d7836c4 11230
cdaed652
VZ
11231 return DoMakeImageBlock(image, imageType);
11232}
11233
11234// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11235bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11236{
11237 if (imageType == wxBITMAP_TYPE_INVALID)
11238 return false; // Could not determine image type
ce00f59b 11239
cdaed652
VZ
11240 return DoMakeImageBlock(image, imageType);
11241}
7fe8059f 11242
cdaed652
VZ
11243// Makes the image block
11244bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11245{
11246 wxMemoryOutputStream memStream;
11247 if (!image.SaveFile(memStream, imageType))
5d7836c4 11248 {
7fe8059f 11249 return false;
5d7836c4 11250 }
ce00f59b 11251
cdaed652
VZ
11252 unsigned char* block = new unsigned char[memStream.GetSize()];
11253 if (!block)
377c1ba4 11254 return false;
ce00f59b 11255
5d7836c4
JS
11256 if (m_data)
11257 delete[] m_data;
cdaed652 11258 m_data = block;
ce00f59b
VZ
11259
11260 m_imageType = imageType;
cdaed652 11261 m_dataSize = memStream.GetSize();
5d7836c4 11262
cdaed652 11263 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
11264
11265 return (m_data != NULL);
11266}
11267
5d7836c4
JS
11268// Write to a file
11269bool wxRichTextImageBlock::Write(const wxString& filename)
11270{
11271 return WriteBlock(filename, m_data, m_dataSize);
11272}
11273
11274void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11275{
11276 m_imageType = block.m_imageType;
5276b0a5 11277 wxDELETEA(m_data);
5d7836c4
JS
11278 m_dataSize = block.m_dataSize;
11279 if (m_dataSize == 0)
11280 return;
11281
11282 m_data = new unsigned char[m_dataSize];
11283 unsigned int i;
11284 for (i = 0; i < m_dataSize; i++)
11285 m_data[i] = block.m_data[i];
11286}
11287
11288//// Operators
11289void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11290{
11291 Copy(block);
11292}
11293
11294// Load a wxImage from the block
11295bool wxRichTextImageBlock::Load(wxImage& image)
11296{
11297 if (!m_data)
7fe8059f 11298 return false;
5d7836c4
JS
11299
11300 // Read in the image.
0ca07313 11301#if wxUSE_STREAMS
5d7836c4
JS
11302 wxMemoryInputStream mstream(m_data, m_dataSize);
11303 bool success = image.LoadFile(mstream, GetImageType());
11304#else
a51e601e
FM
11305 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11306 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11307
11308 if (!WriteBlock(tempFile, m_data, m_dataSize))
11309 {
7fe8059f 11310 return false;
5d7836c4
JS
11311 }
11312 success = image.LoadFile(tempFile, GetImageType());
11313 wxRemoveFile(tempFile);
11314#endif
11315
11316 return success;
11317}
11318
11319// Write data in hex to a stream
11320bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11321{
4dc7ae1a
JS
11322 if (m_dataSize == 0)
11323 return true;
11324
11325 int bufSize = 100000;
a3c12576
JS
11326 if (int(2*m_dataSize) < bufSize)
11327 bufSize = 2*m_dataSize;
4dc7ae1a 11328 char* buf = new char[bufSize+1];
351c0647
JS
11329
11330 int left = m_dataSize;
11331 int n, i, j;
11332 j = 0;
11333 while (left > 0)
5d7836c4 11334 {
351c0647
JS
11335 if (left*2 > bufSize)
11336 {
11337 n = bufSize; left -= (bufSize/2);
11338 }
11339 else
11340 {
11341 n = left*2; left = 0;
11342 }
7fe8059f 11343
351c0647
JS
11344 char* b = buf;
11345 for (i = 0; i < (n/2); i++)
11346 {
f728025e 11347 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
11348 b += 2; j ++;
11349 }
5d7836c4 11350
351c0647
JS
11351 buf[n] = 0;
11352 stream.Write((const char*) buf, n);
11353 }
4dc7ae1a 11354 delete[] buf;
5d7836c4
JS
11355 return true;
11356}
11357
11358// Read data in hex from a stream
d75a69e8 11359bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
11360{
11361 int dataSize = length/2;
11362
11363 if (m_data)
11364 delete[] m_data;
11365
046fce47
FM
11366 // create a null terminated temporary string:
11367 char str[3];
11368 str[2] = '\0';
11369
5d7836c4
JS
11370 m_data = new unsigned char[dataSize];
11371 int i;
11372 for (i = 0; i < dataSize; i ++)
11373 {
c9f78968
VS
11374 str[0] = (char)stream.GetC();
11375 str[1] = (char)stream.GetC();
5d7836c4 11376
a9465653 11377 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
11378 }
11379
11380 m_dataSize = dataSize;
11381 m_imageType = imageType;
11382
11383 return true;
11384}
11385
5d7836c4
JS
11386// Allocate and read from stream as a block of memory
11387unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11388{
11389 unsigned char* block = new unsigned char[size];
11390 if (!block)
11391 return NULL;
11392
11393 stream.Read(block, size);
11394
11395 return block;
11396}
11397
11398unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11399{
11400 wxFileInputStream stream(filename);
a1b806b9 11401 if (!stream.IsOk())
5d7836c4
JS
11402 return NULL;
11403
11404 return ReadBlock(stream, size);
11405}
11406
11407// Write memory block to stream
11408bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11409{
11410 stream.Write((void*) block, size);
11411 return stream.IsOk();
11412
11413}
11414
11415// Write memory block to file
11416bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11417{
11418 wxFileOutputStream outStream(filename);
a1b806b9 11419 if (!outStream.IsOk())
7fe8059f 11420 return false;
5d7836c4
JS
11421
11422 return WriteBlock(outStream, block, size);
11423}
11424
d2d0adc7
JS
11425// Gets the extension for the block's type
11426wxString wxRichTextImageBlock::GetExtension() const
11427{
11428 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11429 if (handler)
11430 return handler->GetExtension();
11431 else
11432 return wxEmptyString;
11433}
11434
0ca07313
JS
11435#if wxUSE_DATAOBJ
11436
11437/*!
11438 * The data object for a wxRichTextBuffer
11439 */
11440
11441const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11442
11443wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11444{
11445 m_richTextBuffer = richTextBuffer;
11446
11447 // this string should uniquely identify our format, but is otherwise
11448 // arbitrary
11449 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11450
11451 SetFormat(m_formatRichTextBuffer);
11452}
11453
11454wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11455{
11456 delete m_richTextBuffer;
11457}
11458
11459// after a call to this function, the richTextBuffer is owned by the caller and it
11460// is responsible for deleting it!
11461wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11462{
11463 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11464 m_richTextBuffer = NULL;
11465
11466 return richTextBuffer;
11467}
11468
11469wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11470{
11471 return m_formatRichTextBuffer;
11472}
11473
11474size_t wxRichTextBufferDataObject::GetDataSize() const
11475{
11476 if (!m_richTextBuffer)
11477 return 0;
11478
11479 wxString bufXML;
11480
11481 {
11482 wxStringOutputStream stream(& bufXML);
11483 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11484 {
11485 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11486 return 0;
11487 }
11488 }
11489
11490#if wxUSE_UNICODE
11491 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11492 return strlen(buffer) + 1;
11493#else
11494 return bufXML.Length()+1;
11495#endif
11496}
11497
11498bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11499{
11500 if (!pBuf || !m_richTextBuffer)
11501 return false;
11502
11503 wxString bufXML;
11504
11505 {
11506 wxStringOutputStream stream(& bufXML);
11507 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11508 {
11509 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11510 return 0;
11511 }
11512 }
11513
11514#if wxUSE_UNICODE
11515 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11516 size_t len = strlen(buffer);
11517 memcpy((char*) pBuf, (const char*) buffer, len);
11518 ((char*) pBuf)[len] = 0;
11519#else
11520 size_t len = bufXML.Length();
11521 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11522 ((char*) pBuf)[len] = 0;
11523#endif
11524
11525 return true;
11526}
11527
11528bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
11529{
5276b0a5 11530 wxDELETE(m_richTextBuffer);
0ca07313
JS
11531
11532 wxString bufXML((const char*) buf, wxConvUTF8);
11533
11534 m_richTextBuffer = new wxRichTextBuffer;
11535
11536 wxStringInputStream stream(bufXML);
11537 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
11538 {
11539 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11540
5276b0a5 11541 wxDELETE(m_richTextBuffer);
0ca07313
JS
11542
11543 return false;
11544 }
11545 return true;
11546}
11547
11548#endif
11549 // wxUSE_DATAOBJ
11550
44cc96a8
JS
11551
11552/*
11553 * wxRichTextFontTable
11554 * Manages quick access to a pool of fonts for rendering rich text
11555 */
11556
d65381ac 11557WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
11558
11559class wxRichTextFontTableData: public wxObjectRefData
11560{
11561public:
11562 wxRichTextFontTableData() {}
11563
32423dd8 11564 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
44cc96a8
JS
11565
11566 wxRichTextFontTableHashMap m_hashMap;
11567};
11568
32423dd8 11569wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
44cc96a8
JS
11570{
11571 wxString facename(fontSpec.GetFontFaceName());
44cc96a8 11572
32423dd8
JS
11573 int fontSize = fontSpec.GetFontSize();
11574 if (fontScale != 1.0)
11575 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
11576
11577 wxString units;
11578 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
11579 units = wxT("px");
11580 else
11581 units = wxT("pt");
11582 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
11583 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
11584 facename.c_str(), (int) fontSpec.GetFontEncoding());
11585
11586 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
44cc96a8
JS
11587 if ( entry == m_hashMap.end() )
11588 {
32423dd8
JS
11589 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
11590 {
b42e4b3e 11591 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
32423dd8
JS
11592 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
11593 font.SetStrikethrough(true);
11594 m_hashMap[spec] = font;
11595 return font;
11596 }
11597 else
11598 {
5a0e33af 11599 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
32423dd8
JS
11600 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
11601 font.SetStrikethrough(true);
11602
11603 m_hashMap[spec] = font;
11604 return font;
11605 }
44cc96a8
JS
11606 }
11607 else
11608 {
11609 return entry->second;
11610 }
11611}
11612
11613IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
11614
11615wxRichTextFontTable::wxRichTextFontTable()
11616{
11617 m_refData = new wxRichTextFontTableData;
32423dd8 11618 m_fontScale = 1.0;
44cc96a8
JS
11619}
11620
11621wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 11622 : wxObject()
44cc96a8
JS
11623{
11624 (*this) = table;
11625}
11626
11627wxRichTextFontTable::~wxRichTextFontTable()
11628{
11629 UnRef();
11630}
11631
11632bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
11633{
11634 return (m_refData == table.m_refData);
11635}
11636
11637void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
11638{
11639 Ref(table);
32423dd8 11640 m_fontScale = table.m_fontScale;
44cc96a8
JS
11641}
11642
24777478 11643wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
11644{
11645 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
11646 if (data)
32423dd8 11647 return data->FindFont(fontSpec, m_fontScale);
44cc96a8
JS
11648 else
11649 return wxFont();
11650}
11651
11652void wxRichTextFontTable::Clear()
11653{
11654 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
11655 if (data)
11656 data->m_hashMap.clear();
11657}
11658
32423dd8
JS
11659void wxRichTextFontTable::SetFontScale(double fontScale)
11660{
11661 if (fontScale != m_fontScale)
11662 Clear();
11663 m_fontScale = fontScale;
11664}
11665
24777478
JS
11666// wxTextBoxAttr
11667
24777478
JS
11668void wxTextBoxAttr::Reset()
11669{
11670 m_flags = 0;
603f702b
JS
11671 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
11672 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
11673 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
11674 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 11675 m_boxStyleName = wxEmptyString;
bec80f4f 11676
24777478
JS
11677 m_margins.Reset();
11678 m_padding.Reset();
11679 m_position.Reset();
11680
603f702b 11681 m_size.Reset();
303f0be7
JS
11682 m_minSize.Reset();
11683 m_maxSize.Reset();
24777478
JS
11684
11685 m_border.Reset();
11686 m_outline.Reset();
11687}
11688
11689// Equality test
11690bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
11691{
11692 return (
11693 m_flags == attr.m_flags &&
11694 m_floatMode == attr.m_floatMode &&
11695 m_clearMode == attr.m_clearMode &&
11696 m_collapseMode == attr.m_collapseMode &&
603f702b 11697 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 11698
24777478
JS
11699 m_margins == attr.m_margins &&
11700 m_padding == attr.m_padding &&
11701 m_position == attr.m_position &&
11702
603f702b 11703 m_size == attr.m_size &&
303f0be7
JS
11704 m_minSize == attr.m_minSize &&
11705 m_maxSize == attr.m_maxSize &&
24777478
JS
11706
11707 m_border == attr.m_border &&
2f987d83
JS
11708 m_outline == attr.m_outline &&
11709
11710 m_boxStyleName == attr.m_boxStyleName
24777478
JS
11711 );
11712}
11713
11714// Partial equality test
32423dd8 11715bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
24777478 11716{
32423dd8
JS
11717 if (!weakTest &&
11718 ((!HasFloatMode() && attr.HasFloatMode()) ||
11719 (!HasClearMode() && attr.HasClearMode()) ||
11720 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
11721 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
11722 (!HasBoxStyleName() && attr.HasBoxStyleName())))
11723 {
11724 return false;
11725 }
24777478
JS
11726 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
11727 return false;
11728
11729 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
11730 return false;
11731
11732 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
11733 return false;
11734
603f702b
JS
11735 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
11736 return false;
11737
2f987d83
JS
11738 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
11739 return false;
11740
24777478
JS
11741 // Position
11742
32423dd8 11743 if (!m_position.EqPartial(attr.m_position, weakTest))
24777478
JS
11744 return false;
11745
303f0be7
JS
11746 // Size
11747
32423dd8 11748 if (!m_size.EqPartial(attr.m_size, weakTest))
303f0be7 11749 return false;
32423dd8 11750 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
303f0be7 11751 return false;
32423dd8 11752 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
303f0be7
JS
11753 return false;
11754
24777478
JS
11755 // Margins
11756
32423dd8 11757 if (!m_margins.EqPartial(attr.m_margins, weakTest))
24777478
JS
11758 return false;
11759
11760 // Padding
11761
32423dd8 11762 if (!m_padding.EqPartial(attr.m_padding, weakTest))
24777478
JS
11763 return false;
11764
11765 // Border
11766
32423dd8 11767 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
24777478
JS
11768 return false;
11769
11770 // Outline
11771
32423dd8 11772 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
24777478
JS
11773 return false;
11774
11775 return true;
11776}
11777
11778// Merges the given attributes. If compareWith
11779// is non-NULL, then it will be used to mask out those attributes that are the same in style
11780// and compareWith, for situations where we don't want to explicitly set inherited attributes.
11781bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
11782{
11783 if (attr.HasFloatMode())
11784 {
11785 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
11786 SetFloatMode(attr.GetFloatMode());
11787 }
11788
11789 if (attr.HasClearMode())
11790 {
11791 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
11792 SetClearMode(attr.GetClearMode());
11793 }
11794
11795 if (attr.HasCollapseBorders())
11796 {
11797 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
11798 SetCollapseBorders(attr.GetCollapseBorders());
11799 }
11800
11801 if (attr.HasVerticalAlignment())
11802 {
11803 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
11804 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 11805 }
bec80f4f 11806
2f987d83
JS
11807 if (attr.HasBoxStyleName())
11808 {
11809 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
11810 SetBoxStyleName(attr.GetBoxStyleName());
11811 }
11812
bec80f4f
JS
11813 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
11814 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
11815 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 11816
603f702b 11817 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
11818 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
11819 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 11820
bec80f4f
JS
11821 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
11822 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
11823
11824 return true;
11825}
11826
11827// Remove specified attributes from this object
11828bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
11829{
11830 if (attr.HasFloatMode())
11831 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11832
11833 if (attr.HasClearMode())
11834 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11835
11836 if (attr.HasCollapseBorders())
11837 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11838
603f702b
JS
11839 if (attr.HasVerticalAlignment())
11840 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11841
2f987d83
JS
11842 if (attr.HasBoxStyleName())
11843 {
11844 SetBoxStyleName(wxEmptyString);
11845 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11846 }
11847
24777478
JS
11848 m_margins.RemoveStyle(attr.m_margins);
11849 m_padding.RemoveStyle(attr.m_padding);
11850 m_position.RemoveStyle(attr.m_position);
11851
603f702b 11852 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
11853 m_minSize.RemoveStyle(attr.m_minSize);
11854 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
11855
11856 m_border.RemoveStyle(attr.m_border);
11857 m_outline.RemoveStyle(attr.m_outline);
11858
11859 return true;
11860}
11861
11862// Collects the attributes that are common to a range of content, building up a note of
11863// which attributes are absent in some objects and which clash in some objects.
11864void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
11865{
11866 if (attr.HasFloatMode())
11867 {
11868 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
11869 {
11870 if (HasFloatMode())
11871 {
11872 if (GetFloatMode() != attr.GetFloatMode())
11873 {
11874 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
11875 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11876 }
11877 }
11878 else
11879 SetFloatMode(attr.GetFloatMode());
11880 }
11881 }
11882 else
11883 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 11884
24777478
JS
11885 if (attr.HasClearMode())
11886 {
11887 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
11888 {
11889 if (HasClearMode())
11890 {
11891 if (GetClearMode() != attr.GetClearMode())
11892 {
11893 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11894 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11895 }
11896 }
11897 else
11898 SetClearMode(attr.GetClearMode());
11899 }
11900 }
11901 else
11902 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11903
11904 if (attr.HasCollapseBorders())
11905 {
11906 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
11907 {
11908 if (HasCollapseBorders())
11909 {
11910 if (GetCollapseBorders() != attr.GetCollapseBorders())
11911 {
11912 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11913 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11914 }
11915 }
11916 else
11917 SetCollapseBorders(attr.GetCollapseBorders());
11918 }
11919 }
11920 else
11921 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 11922
603f702b
JS
11923 if (attr.HasVerticalAlignment())
11924 {
11925 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
11926 {
11927 if (HasVerticalAlignment())
11928 {
11929 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
11930 {
11931 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11932 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11933 }
11934 }
11935 else
11936 SetVerticalAlignment(attr.GetVerticalAlignment());
11937 }
11938 }
11939 else
11940 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11941
2f987d83
JS
11942 if (attr.HasBoxStyleName())
11943 {
11944 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
11945 {
11946 if (HasBoxStyleName())
11947 {
11948 if (GetBoxStyleName() != attr.GetBoxStyleName())
11949 {
11950 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11951 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11952 }
11953 }
11954 else
11955 SetBoxStyleName(attr.GetBoxStyleName());
11956 }
11957 }
11958 else
11959 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11960
24777478
JS
11961 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
11962 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
11963 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
11964
603f702b 11965 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
11966 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
11967 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
11968
11969 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
11970 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
11971}
11972
eb3d8a33
JS
11973bool wxTextBoxAttr::IsDefault() const
11974{
11975 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 11976 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
11977 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
11978}
11979
24777478
JS
11980// wxRichTextAttr
11981
11982void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
11983{
bec80f4f
JS
11984 wxTextAttr::Copy(attr);
11985
24777478
JS
11986 m_textBoxAttr = attr.m_textBoxAttr;
11987}
11988
11989bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
11990{
11991 if (!(wxTextAttr::operator==(attr)))
11992 return false;
bec80f4f 11993
24777478
JS
11994 return (m_textBoxAttr == attr.m_textBoxAttr);
11995}
11996
32423dd8
JS
11997// Partial equality test
11998bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
24777478 11999{
32423dd8 12000 if (!(wxTextAttr::EqPartial(attr, weakTest)))
24777478 12001 return false;
bec80f4f 12002
32423dd8 12003 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
24777478
JS
12004}
12005
12006// Merges the given attributes. If compareWith
12007// is non-NULL, then it will be used to mask out those attributes that are the same in style
12008// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12009bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12010{
12011 wxTextAttr::Apply(style, compareWith);
12012
12013 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12014}
12015
12016// Remove specified attributes from this object
12017bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12018{
12019 wxTextAttr::RemoveStyle(*this, attr);
12020
12021 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12022}
12023
12024// Collects the attributes that are common to a range of content, building up a note of
12025// which attributes are absent in some objects and which clash in some objects.
12026void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12027{
12028 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 12029
24777478
JS
12030 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12031}
12032
12033// Partial equality test
32423dd8 12034bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
24777478 12035{
32423dd8
JS
12036 if (!weakTest &&
12037 ((!HasStyle() && border.HasStyle()) ||
12038 (!HasColour() && border.HasColour()) ||
12039 (!HasWidth() && border.HasWidth())))
12040 {
12041 return false;
12042 }
12043
12044 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
24777478
JS
12045 return false;
12046
32423dd8 12047 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
24777478
JS
12048 return false;
12049
32423dd8 12050 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
24777478
JS
12051 return false;
12052
12053 return true;
12054}
12055
12056// Apply border to 'this', but not if the same as compareWith
bec80f4f 12057bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
12058{
12059 if (border.HasStyle())
12060 {
12061 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12062 SetStyle(border.GetStyle());
12063 }
12064 if (border.HasColour())
12065 {
12066 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12067 SetColour(border.GetColourLong());
12068 }
12069 if (border.HasWidth())
12070 {
12071 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12072 SetWidth(border.GetWidth());
12073 }
12074
12075 return true;
12076}
12077
12078// Remove specified attributes from this object
bec80f4f 12079bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
12080{
12081 if (attr.HasStyle() && HasStyle())
12082 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12083 if (attr.HasColour() && HasColour())
12084 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12085 if (attr.HasWidth() && HasWidth())
12086 m_borderWidth.Reset();
12087
12088 return true;
12089}
12090
12091// Collects the attributes that are common to a range of content, building up a note of
12092// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12093void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
12094{
12095 if (attr.HasStyle())
12096 {
12097 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12098 {
12099 if (HasStyle())
12100 {
12101 if (GetStyle() != attr.GetStyle())
12102 {
12103 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12104 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12105 }
12106 }
12107 else
12108 SetStyle(attr.GetStyle());
12109 }
12110 }
12111 else
12112 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12113
12114 if (attr.HasColour())
12115 {
12116 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12117 {
12118 if (HasColour())
12119 {
12120 if (GetColour() != attr.GetColour())
12121 {
12122 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12123 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12124 }
12125 }
12126 else
12127 SetColour(attr.GetColourLong());
12128 }
12129 }
12130 else
12131 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 12132
24777478
JS
12133 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12134}
12135
12136// Partial equality test
32423dd8 12137bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
24777478 12138{
32423dd8
JS
12139 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12140 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
24777478
JS
12141}
12142
12143// Apply border to 'this', but not if the same as compareWith
bec80f4f 12144bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 12145{
bec80f4f
JS
12146 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12147 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12148 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12149 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
12150 return true;
12151}
12152
12153// Remove specified attributes from this object
bec80f4f 12154bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
12155{
12156 m_left.RemoveStyle(attr.m_left);
12157 m_right.RemoveStyle(attr.m_right);
12158 m_top.RemoveStyle(attr.m_top);
12159 m_bottom.RemoveStyle(attr.m_bottom);
12160 return true;
12161}
12162
12163// Collects the attributes that are common to a range of content, building up a note of
12164// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12165void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
12166{
12167 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12168 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12169 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12170 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12171}
12172
12173// Set style of all borders
bec80f4f 12174void wxTextAttrBorders::SetStyle(int style)
24777478
JS
12175{
12176 m_left.SetStyle(style);
12177 m_right.SetStyle(style);
12178 m_top.SetStyle(style);
12179 m_bottom.SetStyle(style);
12180}
12181
12182// Set colour of all borders
bec80f4f 12183void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
12184{
12185 m_left.SetColour(colour);
12186 m_right.SetColour(colour);
12187 m_top.SetColour(colour);
12188 m_bottom.SetColour(colour);
12189}
12190
bec80f4f 12191void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
12192{
12193 m_left.SetColour(colour);
12194 m_right.SetColour(colour);
12195 m_top.SetColour(colour);
12196 m_bottom.SetColour(colour);
12197}
12198
12199// Set width of all borders
bec80f4f 12200void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
12201{
12202 m_left.SetWidth(width);
12203 m_right.SetWidth(width);
12204 m_top.SetWidth(width);
12205 m_bottom.SetWidth(width);
12206}
12207
12208// Partial equality test
32423dd8 12209bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
24777478 12210{
32423dd8
JS
12211 if (!weakTest && !IsValid() && dim.IsValid())
12212 return false;
12213
603f702b 12214 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
12215 return false;
12216 else
12217 return true;
12218}
12219
12220bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12221{
603f702b 12222 if (dim.IsValid())
24777478
JS
12223 {
12224 if (!(compareWith && dim == (*compareWith)))
12225 (*this) = dim;
12226 }
12227
12228 return true;
12229}
12230
12231// Collects the attributes that are common to a range of content, building up a note of
12232// which attributes are absent in some objects and which clash in some objects.
12233void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12234{
603f702b 12235 if (attr.IsValid())
24777478 12236 {
603f702b 12237 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 12238 {
603f702b 12239 if (IsValid())
24777478
JS
12240 {
12241 if (!((*this) == attr))
12242 {
603f702b
JS
12243 clashingAttr.SetValid(true);
12244 SetValid(false);
24777478
JS
12245 }
12246 }
12247 else
12248 (*this) = attr;
12249 }
12250 }
12251 else
603f702b 12252 absentAttr.SetValid(true);
24777478
JS
12253}
12254
8995db52
JS
12255wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12256{
12257 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12258}
12259
12260wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12261{
12262 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12263}
12264
bec80f4f
JS
12265int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12266{
12267 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12268}
12269
12270int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12271{
12272 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12273}
12274
12275int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12276{
12277 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12278 return ConvertTenthsMMToPixels(dim.GetValue());
12279 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12280 return dim.GetValue();
12281 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12282 {
12283 wxASSERT(m_parentSize != wxDefaultSize);
12284 if (direction == wxHORIZONTAL)
12285 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12286 else
12287 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12288 }
12289 else
12290 {
12291 wxASSERT(false);
12292 return 0;
12293 }
12294}
12295
12296int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12297{
12298 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12299 return dim.GetValue();
12300 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12301 return ConvertPixelsToTenthsMM(dim.GetValue());
12302 else
12303 {
12304 wxASSERT(false);
12305 return 0;
12306 }
12307}
12308
24777478 12309// Partial equality test
32423dd8 12310bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
24777478 12311{
32423dd8 12312 if (!m_left.EqPartial(dims.m_left, weakTest))
24777478
JS
12313 return false;
12314
32423dd8 12315 if (!m_right.EqPartial(dims.m_right, weakTest))
24777478
JS
12316 return false;
12317
32423dd8 12318 if (!m_top.EqPartial(dims.m_top, weakTest))
24777478
JS
12319 return false;
12320
32423dd8 12321 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
24777478
JS
12322 return false;
12323
12324 return true;
12325}
12326
12327// Apply border to 'this', but not if the same as compareWith
bec80f4f 12328bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
12329{
12330 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12331 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12332 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12333 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12334
12335 return true;
12336}
12337
12338// Remove specified attributes from this object
bec80f4f 12339bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 12340{
603f702b 12341 if (attr.m_left.IsValid())
24777478 12342 m_left.Reset();
603f702b 12343 if (attr.m_right.IsValid())
24777478 12344 m_right.Reset();
603f702b 12345 if (attr.m_top.IsValid())
24777478 12346 m_top.Reset();
603f702b 12347 if (attr.m_bottom.IsValid())
24777478
JS
12348 m_bottom.Reset();
12349
12350 return true;
12351}
12352
12353// Collects the attributes that are common to a range of content, building up a note of
12354// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12355void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
12356{
12357 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12358 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12359 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12360 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12361}
12362
603f702b 12363// Partial equality test
32423dd8 12364bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
603f702b 12365{
32423dd8 12366 if (!m_width.EqPartial(size.m_width, weakTest))
603f702b
JS
12367 return false;
12368
32423dd8 12369 if (!m_height.EqPartial(size.m_height, weakTest))
603f702b
JS
12370 return false;
12371
12372 return true;
12373}
12374
12375// Apply border to 'this', but not if the same as compareWith
12376bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12377{
12378 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12379 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12380
12381 return true;
12382}
12383
12384// Remove specified attributes from this object
12385bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12386{
12387 if (attr.m_width.IsValid())
12388 m_width.Reset();
12389 if (attr.m_height.IsValid())
12390 m_height.Reset();
12391
12392 return true;
12393}
12394
12395// Collects the attributes that are common to a range of content, building up a note of
12396// which attributes are absent in some objects and which clash in some objects.
12397void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12398{
12399 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12400 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12401}
12402
24777478
JS
12403// Collects the attributes that are common to a range of content, building up a note of
12404// which attributes are absent in some objects and which clash in some objects.
12405void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12406{
12407 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12408 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12409
12410 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12411
12412 if (attr.HasFont())
12413 {
340ef5c5
JS
12414 // If different font size units are being used, this is a clash.
12415 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
24777478 12416 {
340ef5c5
JS
12417 currentStyle.SetFontSize(0);
12418 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12419 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12420 }
12421 else
12422 {
12423 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
24777478 12424 {
340ef5c5 12425 if (currentStyle.HasFontPointSize())
24777478 12426 {
340ef5c5
JS
12427 if (currentStyle.GetFontSize() != attr.GetFontSize())
12428 {
12429 // Clash of attr - mark as such
12430 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12431 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12432 }
24777478 12433 }
340ef5c5
JS
12434 else
12435 currentStyle.SetFontSize(attr.GetFontSize());
24777478 12436 }
24777478 12437
340ef5c5 12438 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
32423dd8 12439 {
340ef5c5 12440 if (currentStyle.HasFontPixelSize())
32423dd8 12441 {
340ef5c5
JS
12442 if (currentStyle.GetFontSize() != attr.GetFontSize())
12443 {
12444 // Clash of attr - mark as such
12445 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12446 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12447 }
32423dd8 12448 }
340ef5c5
JS
12449 else
12450 currentStyle.SetFontPixelSize(attr.GetFontSize());
32423dd8 12451 }
32423dd8
JS
12452 }
12453
24777478
JS
12454 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12455 {
12456 if (currentStyle.HasFontItalic())
12457 {
12458 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
12459 {
12460 // Clash of attr - mark as such
12461 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12462 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12463 }
12464 }
12465 else
12466 currentStyle.SetFontStyle(attr.GetFontStyle());
12467 }
12468
12469 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12470 {
12471 if (currentStyle.HasFontFamily())
12472 {
12473 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
12474 {
12475 // Clash of attr - mark as such
12476 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12477 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12478 }
12479 }
12480 else
12481 currentStyle.SetFontFamily(attr.GetFontFamily());
12482 }
12483
12484 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12485 {
12486 if (currentStyle.HasFontWeight())
12487 {
12488 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
12489 {
12490 // Clash of attr - mark as such
12491 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12492 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12493 }
12494 }
12495 else
12496 currentStyle.SetFontWeight(attr.GetFontWeight());
12497 }
12498
12499 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12500 {
12501 if (currentStyle.HasFontFaceName())
12502 {
12503 wxString faceName1(currentStyle.GetFontFaceName());
12504 wxString faceName2(attr.GetFontFaceName());
12505
12506 if (faceName1 != faceName2)
12507 {
12508 // Clash of attr - mark as such
12509 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
12510 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
12511 }
12512 }
12513 else
12514 currentStyle.SetFontFaceName(attr.GetFontFaceName());
12515 }
12516
12517 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
12518 {
12519 if (currentStyle.HasFontUnderlined())
12520 {
12521 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
12522 {
12523 // Clash of attr - mark as such
12524 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12525 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12526 }
12527 }
12528 else
12529 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
12530 }
32423dd8
JS
12531
12532 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
12533 {
12534 if (currentStyle.HasFontStrikethrough())
12535 {
12536 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
12537 {
12538 // Clash of attr - mark as such
12539 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12540 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12541 }
12542 }
12543 else
12544 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
12545 }
24777478
JS
12546 }
12547
12548 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
12549 {
12550 if (currentStyle.HasTextColour())
12551 {
12552 if (currentStyle.GetTextColour() != attr.GetTextColour())
12553 {
12554 // Clash of attr - mark as such
12555 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
12556 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
12557 }
12558 }
12559 else
12560 currentStyle.SetTextColour(attr.GetTextColour());
12561 }
12562
12563 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
12564 {
12565 if (currentStyle.HasBackgroundColour())
12566 {
12567 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
12568 {
12569 // Clash of attr - mark as such
12570 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
12571 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
12572 }
12573 }
12574 else
12575 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
12576 }
12577
12578 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
12579 {
12580 if (currentStyle.HasAlignment())
12581 {
12582 if (currentStyle.GetAlignment() != attr.GetAlignment())
12583 {
12584 // Clash of attr - mark as such
12585 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
12586 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
12587 }
12588 }
12589 else
12590 currentStyle.SetAlignment(attr.GetAlignment());
12591 }
12592
12593 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
12594 {
12595 if (currentStyle.HasTabs())
12596 {
12597 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
12598 {
12599 // Clash of attr - mark as such
12600 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
12601 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
12602 }
12603 }
12604 else
12605 currentStyle.SetTabs(attr.GetTabs());
12606 }
12607
12608 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
12609 {
12610 if (currentStyle.HasLeftIndent())
12611 {
12612 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
12613 {
12614 // Clash of attr - mark as such
12615 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
12616 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
12617 }
12618 }
12619 else
12620 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
12621 }
12622
12623 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
12624 {
12625 if (currentStyle.HasRightIndent())
12626 {
12627 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
12628 {
12629 // Clash of attr - mark as such
12630 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
12631 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
12632 }
12633 }
12634 else
12635 currentStyle.SetRightIndent(attr.GetRightIndent());
12636 }
12637
12638 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
12639 {
12640 if (currentStyle.HasParagraphSpacingAfter())
12641 {
12642 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
12643 {
12644 // Clash of attr - mark as such
12645 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
12646 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
12647 }
12648 }
12649 else
12650 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
12651 }
12652
12653 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
12654 {
12655 if (currentStyle.HasParagraphSpacingBefore())
12656 {
12657 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
12658 {
12659 // Clash of attr - mark as such
12660 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
12661 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
12662 }
12663 }
12664 else
12665 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
12666 }
12667
12668 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
12669 {
12670 if (currentStyle.HasLineSpacing())
12671 {
12672 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
12673 {
12674 // Clash of attr - mark as such
12675 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
12676 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
12677 }
12678 }
12679 else
12680 currentStyle.SetLineSpacing(attr.GetLineSpacing());
12681 }
12682
12683 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
12684 {
12685 if (currentStyle.HasCharacterStyleName())
12686 {
12687 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
12688 {
12689 // Clash of attr - mark as such
12690 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
12691 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
12692 }
12693 }
12694 else
12695 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
12696 }
12697
12698 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
12699 {
12700 if (currentStyle.HasParagraphStyleName())
12701 {
12702 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
12703 {
12704 // Clash of attr - mark as such
12705 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
12706 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
12707 }
12708 }
12709 else
12710 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
12711 }
12712
12713 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
12714 {
12715 if (currentStyle.HasListStyleName())
12716 {
12717 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
12718 {
12719 // Clash of attr - mark as such
12720 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
12721 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
12722 }
12723 }
12724 else
12725 currentStyle.SetListStyleName(attr.GetListStyleName());
12726 }
12727
12728 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
12729 {
12730 if (currentStyle.HasBulletStyle())
12731 {
12732 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
12733 {
12734 // Clash of attr - mark as such
12735 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
12736 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
12737 }
12738 }
12739 else
12740 currentStyle.SetBulletStyle(attr.GetBulletStyle());
12741 }
12742
12743 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
12744 {
12745 if (currentStyle.HasBulletNumber())
12746 {
12747 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
12748 {
12749 // Clash of attr - mark as such
12750 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
12751 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
12752 }
12753 }
12754 else
12755 currentStyle.SetBulletNumber(attr.GetBulletNumber());
12756 }
12757
12758 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
12759 {
12760 if (currentStyle.HasBulletText())
12761 {
12762 if (currentStyle.GetBulletText() != attr.GetBulletText())
12763 {
12764 // Clash of attr - mark as such
12765 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
12766 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
12767 }
12768 }
12769 else
12770 {
12771 currentStyle.SetBulletText(attr.GetBulletText());
12772 currentStyle.SetBulletFont(attr.GetBulletFont());
12773 }
12774 }
12775
12776 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
12777 {
12778 if (currentStyle.HasBulletName())
12779 {
12780 if (currentStyle.GetBulletName() != attr.GetBulletName())
12781 {
12782 // Clash of attr - mark as such
12783 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
12784 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
12785 }
12786 }
12787 else
12788 {
12789 currentStyle.SetBulletName(attr.GetBulletName());
12790 }
12791 }
12792
12793 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
12794 {
12795 if (currentStyle.HasURL())
12796 {
12797 if (currentStyle.GetURL() != attr.GetURL())
12798 {
12799 // Clash of attr - mark as such
12800 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
12801 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
12802 }
12803 }
12804 else
12805 {
12806 currentStyle.SetURL(attr.GetURL());
12807 }
12808 }
12809
12810 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
12811 {
12812 if (currentStyle.HasTextEffects())
12813 {
12814 // We need to find the bits in the new attr that are different:
12815 // just look at those bits that are specified by the new attr.
12816
12817 // We need to remove the bits and flags that are not common between current attr
12818 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12819 // previous styles.
12820
12821 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
12822 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
12823
12824 if (currentRelevantTextEffects != newRelevantTextEffects)
12825 {
12826 // Find the text effects that were different, using XOR
12827 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
12828
12829 // Clash of attr - mark as such
12830 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
12831 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
12832 }
12833 }
12834 else
12835 {
12836 currentStyle.SetTextEffects(attr.GetTextEffects());
12837 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
12838 }
12839
12840 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12841 // that we've looked at so far
12842 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
12843 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
12844
12845 if (currentStyle.GetTextEffectFlags() == 0)
12846 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
12847 }
12848
12849 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
12850 {
12851 if (currentStyle.HasOutlineLevel())
12852 {
12853 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
12854 {
12855 // Clash of attr - mark as such
12856 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12857 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12858 }
12859 }
12860 else
12861 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
12862 }
12863}
12864
bec80f4f
JS
12865WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
12866
12867IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
12868
12869bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
12870{
12871 if (m_properties.GetCount() != props.GetCount())
12872 return false;
12873
12874 size_t i;
12875 for (i = 0; i < m_properties.GetCount(); i++)
12876 {
12877 const wxVariant& var1 = m_properties[i];
12878 int idx = props.Find(var1.GetName());
12879 if (idx == -1)
12880 return false;
12881 const wxVariant& var2 = props.m_properties[idx];
12882 if (!(var1 == var2))
12883 return false;
12884 }
12885
12886 return true;
12887}
12888
12889wxArrayString wxRichTextProperties::GetPropertyNames() const
12890{
12891 wxArrayString arr;
12892 size_t i;
12893 for (i = 0; i < m_properties.GetCount(); i++)
12894 {
12895 arr.Add(m_properties[i].GetName());
12896 }
12897 return arr;
12898}
12899
12900int wxRichTextProperties::Find(const wxString& name) const
12901{
12902 size_t i;
12903 for (i = 0; i < m_properties.GetCount(); i++)
12904 {
12905 if (m_properties[i].GetName() == name)
12906 return (int) i;
12907 }
12908 return -1;
12909}
12910
590a0f8b
JS
12911bool wxRichTextProperties::Remove(const wxString& name)
12912{
12913 int idx = Find(name);
12914 if (idx != -1)
12915 {
12916 m_properties.RemoveAt(idx);
12917 return true;
12918 }
12919 else
12920 return false;
12921}
12922
bec80f4f
JS
12923wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
12924{
12925 int idx = Find(name);
12926 if (idx == wxNOT_FOUND)
12927 SetProperty(name, wxString());
12928 idx = Find(name);
12929 if (idx != wxNOT_FOUND)
12930 {
12931 return & (*this)[idx];
12932 }
12933 else
12934 return NULL;
12935}
12936
12937const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
12938{
12939 static const wxVariant nullVariant;
12940 int idx = Find(name);
12941 if (idx != -1)
12942 return m_properties[idx];
12943 else
12944 return nullVariant;
12945}
12946
12947wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
12948{
12949 return GetProperty(name).GetString();
12950}
12951
12952long wxRichTextProperties::GetPropertyLong(const wxString& name) const
12953{
12954 return GetProperty(name).GetLong();
12955}
12956
12957bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
12958{
12959 return GetProperty(name).GetBool();
12960}
12961
12962double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
12963{
12964 return GetProperty(name).GetDouble();
12965}
12966
12967void wxRichTextProperties::SetProperty(const wxVariant& variant)
12968{
12969 wxASSERT(!variant.GetName().IsEmpty());
12970
12971 int idx = Find(variant.GetName());
12972
12973 if (idx == -1)
12974 m_properties.Add(variant);
12975 else
12976 m_properties[idx] = variant;
12977}
12978
12979void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
12980{
12981 int idx = Find(name);
12982 wxVariant var(variant);
12983 var.SetName(name);
12984
12985 if (idx == -1)
12986 m_properties.Add(var);
12987 else
12988 m_properties[idx] = var;
12989}
12990
12991void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
12992{
12993 SetProperty(name, wxVariant(value, name));
12994}
12995
12996void wxRichTextProperties::SetProperty(const wxString& name, long value)
12997{
12998 SetProperty(name, wxVariant(value, name));
12999}
13000
13001void wxRichTextProperties::SetProperty(const wxString& name, double value)
13002{
13003 SetProperty(name, wxVariant(value, name));
13004}
13005
13006void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13007{
13008 SetProperty(name, wxVariant(value, name));
13009}
24777478 13010
590a0f8b
JS
13011void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13012{
13013 size_t i;
13014 for (i = 0; i < properties.GetCount(); i++)
13015 {
13016 wxString name = properties.GetProperties()[i].GetName();
13017 if (HasProperty(name))
13018 Remove(name);
13019 }
13020}
13021
13022void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13023{
13024 size_t i;
13025 for (i = 0; i < properties.GetCount(); i++)
13026 {
13027 SetProperty(properties.GetProperties()[i]);
13028 }
13029}
13030
603f702b
JS
13031wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13032{
13033 if (m_address.GetCount() == 0)
13034 return topLevelContainer;
13035
13036 wxRichTextCompositeObject* p = topLevelContainer;
13037 size_t i = 0;
13038 while (p && i < m_address.GetCount())
13039 {
13040 int pos = m_address[i];
13041 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13042 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13043 return NULL;
13044
13045 wxRichTextObject* p1 = p->GetChild(pos);
13046 if (i == (m_address.GetCount()-1))
13047 return p1;
13048
13049 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13050 i ++;
13051 }
13052 return NULL;
13053}
13054
13055bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13056{
13057 m_address.Clear();
13058
13059 if (topLevelContainer == obj)
13060 return true;
13061
13062 wxRichTextObject* o = obj;
13063 while (o)
13064 {
13065 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13066 if (!p)
13067 return false;
13068
13069 int pos = p->GetChildren().IndexOf(o);
13070 if (pos == -1)
13071 return false;
13072
13073 m_address.Insert(pos, 0);
13074
13075 if (p == topLevelContainer)
13076 return true;
13077
13078 o = p;
13079 }
13080 return false;
13081}
13082
13083// Equality test
13084bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13085{
13086 if (m_container != sel.m_container)
13087 return false;
13088 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13089 return false;
13090 size_t i;
13091 for (i = 0; i < m_ranges.GetCount(); i++)
13092 if (!(m_ranges[i] == sel.m_ranges[i]))
13093 return false;
13094 return true;
13095}
13096
13097// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13098// or none at the level of the object's container.
13099wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13100{
13101 if (IsValid())
13102 {
13103 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13104
13105 if (container == m_container)
13106 return m_ranges;
13107
13108 container = obj->GetContainer();
13109 while (container)
13110 {
13111 if (container->GetParent())
13112 {
13113 // If we found that our object's container is within the range of
13114 // a selection higher up, then assume the whole original object
13115 // is also selected.
13116 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13117 if (parentContainer == m_container)
13118 {
13119 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13120 {
13121 wxRichTextRangeArray ranges;
13122 ranges.Add(obj->GetRange());
13123 return ranges;
13124 }
13125 }
13126
13127 container = parentContainer;
13128 }
13129 else
13130 {
13131 container = NULL;
13132 break;
13133 }
13134 }
13135 }
13136 return wxRichTextRangeArray();
13137}
13138
13139// Is the given position within the selection?
13140bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13141{
13142 if (!IsValid())
13143 return false;
13144 else
13145 {
13146 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13147 return WithinSelection(pos, selectionRanges);
13148 }
13149}
13150
13151// Is the given position within the selection range?
13152bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13153{
13154 size_t i;
13155 for (i = 0; i < ranges.GetCount(); i++)
13156 {
13157 const wxRichTextRange& range = ranges[i];
13158 if (pos >= range.GetStart() && pos <= range.GetEnd())
13159 return true;
13160 }
13161 return false;
13162}
13163
13164// Is the given range completely within the selection range?
13165bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13166{
13167 size_t i;
13168 for (i = 0; i < ranges.GetCount(); i++)
13169 {
13170 const wxRichTextRange& eachRange = ranges[i];
13171 if (range.IsWithin(eachRange))
13172 return true;
13173 }
13174 return false;
13175}
13176
8db2e3ef
JS
13177IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13178IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13179
13180bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13181{
13182 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13183 while (node)
13184 {
13185 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13186 if (handler->HasVirtualAttributes(obj))
13187 return true;
13188
13189 node = node->GetNext();
13190 }
13191 return false;
13192}
13193
13194wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13195{
13196 wxRichTextAttr attr;
13197 // We apply all handlers, so we can may combine several different attributes
13198 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13199 while (node)
13200 {
13201 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13202 if (handler->HasVirtualAttributes(obj))
13203 {
13204 bool success = handler->GetVirtualAttributes(attr, obj);
13205 wxASSERT(success);
aa8f57f4 13206 wxUnusedVar(success);
8db2e3ef
JS
13207 }
13208
13209 node = node->GetNext();
13210 }
13211 return attr;
13212}
13213
13214bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13215{
13216 if (HasVirtualAttributes(obj))
13217 {
13218 wxRichTextAttr a(GetVirtualAttributes(obj));
13219 attr.Apply(a);
13220 return true;
13221 }
13222 else
13223 return false;
13224}
13225
13226/// Adds a handler to the end
13227void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13228{
13229 sm_drawingHandlers.Append(handler);
13230}
13231
13232/// Inserts a handler at the front
13233void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13234{
13235 sm_drawingHandlers.Insert( handler );
13236}
13237
13238/// Removes a handler
13239bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13240{
13241 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13242 if (handler)
13243 {
13244 sm_drawingHandlers.DeleteObject(handler);
13245 delete handler;
13246 return true;
13247 }
13248 else
13249 return false;
13250}
13251
13252wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13253{
13254 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13255 while (node)
13256 {
13257 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13258 if (handler->GetName().Lower() == name.Lower()) return handler;
13259
13260 node = node->GetNext();
13261 }
13262 return NULL;
13263}
13264
13265void wxRichTextBuffer::CleanUpDrawingHandlers()
13266{
13267 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13268 while (node)
13269 {
13270 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13271 wxList::compatibility_iterator next = node->GetNext();
13272 delete handler;
13273 node = next;
13274 }
13275
13276 sm_drawingHandlers.Clear();
13277}
603f702b 13278
7c9fdebe
JS
13279void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13280{
13281 sm_fieldTypes[fieldType->GetName()] = fieldType;
13282}
13283
13284bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13285{
13286 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13287 if (it == sm_fieldTypes.end())
13288 return false;
13289 else
13290 {
13291 wxRichTextFieldType* fieldType = it->second;
13292 sm_fieldTypes.erase(it);
13293 delete fieldType;
13294 return true;
13295 }
13296}
13297
13298wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13299{
13300 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13301 if (it == sm_fieldTypes.end())
13302 return NULL;
13303 else
13304 return it->second;
13305}
13306
13307void wxRichTextBuffer::CleanUpFieldTypes()
13308{
13309 wxRichTextFieldTypeHashMap::iterator it;
13310 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
13311 {
13312 wxRichTextFieldType* fieldType = it->second;
13313 delete fieldType;
13314 }
13315
13316 sm_fieldTypes.clear();
13317}
13318
5d7836c4
JS
13319#endif
13320 // wxUSE_RICHTEXT