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