]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
non-pch build fix
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
CommitLineData
5d7836c4 1/////////////////////////////////////////////////////////////////////////////
61399247 2// Name: src/richtext/richtextbuffer.cpp
5d7836c4
JS
3// Purpose: Buffer for wxRichTextCtrl
4// Author: Julian Smart
7fe8059f 5// Modified by:
5d7836c4 6// Created: 2005-09-30
7fe8059f 7// RCS-ID: $Id$
5d7836c4
JS
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
2be72ac2 11
5d7836c4
JS
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
61399247 16 #pragma hdrstop
5d7836c4
JS
17#endif
18
b01ca8b6
JS
19#if wxUSE_RICHTEXT
20
21#include "wx/richtext/richtextbuffer.h"
22
5d7836c4 23#ifndef WX_PRECOMP
61399247
WS
24 #include "wx/dc.h"
25 #include "wx/intl.h"
7947a48a 26 #include "wx/log.h"
28f92d74 27 #include "wx/dataobj.h"
02761f6c 28 #include "wx/module.h"
5d7836c4
JS
29#endif
30
0ec6da02 31#include "wx/settings.h"
5d7836c4
JS
32#include "wx/filename.h"
33#include "wx/clipbrd.h"
34#include "wx/wfstream.h"
5d7836c4
JS
35#include "wx/mstream.h"
36#include "wx/sstream.h"
0ca07313 37#include "wx/textfile.h"
44cc96a8 38#include "wx/hashmap.h"
cdaed652 39#include "wx/dynarray.h"
5d7836c4 40
5d7836c4
JS
41#include "wx/richtext/richtextctrl.h"
42#include "wx/richtext/richtextstyles.h"
cdaed652 43#include "wx/richtext/richtextimagedlg.h"
603f702b 44#include "wx/richtext/richtextsizepage.h"
1aca9fcd 45#include "wx/richtext/richtextxml.h"
5d7836c4
JS
46
47#include "wx/listimpl.cpp"
bec80f4f 48#include "wx/arrimpl.cpp"
5d7836c4 49
412e0d47
DS
50WX_DEFINE_LIST(wxRichTextObjectList)
51WX_DEFINE_LIST(wxRichTextLineList)
5d7836c4 52
ea160b2e
JS
53// Switch off if the platform doesn't like it for some reason
54#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55
31778480
JS
56// Use GetPartialTextExtents for platforms that support it natively
57#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58
ff76711f
JS
59const wxChar wxRichTextLineBreakChar = (wxChar) 29;
60
cdaed652
VZ
61// Helper classes for floating layout
62struct wxRichTextFloatRectMap
63{
64 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
65 {
66 startY = sY;
67 endY = eY;
68 width = w;
69 anchor = obj;
70 }
71
72 int startY, endY;
73 int width;
74 wxRichTextObject* anchor;
75};
76
77WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
78
79int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
80{
81 return r1->startY - r2->startY;
82}
83
84class wxRichTextFloatCollector
85{
86public:
603f702b 87 wxRichTextFloatCollector(const wxRect& availableRect);
cdaed652
VZ
88 ~wxRichTextFloatCollector();
89
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph* para);
92 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
93
94 // Return the last paragraph we collected
95 wxRichTextParagraph* LastParagraph();
96
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect GetAvailableRect(int startY, int endY);
100
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction, int start, int height) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
104
105 // Find the last y position
106 int GetLastRectBottom();
107
108 // Draw the floats inside a rect
8db2e3ef 109 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652
VZ
110
111 // HitTest the floats
8db2e3ef 112 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
603f702b
JS
113
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
116
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
cdaed652 119
07d4142f
JS
120 // Delete a float
121 bool DeleteFloat(wxRichTextObject* obj);
122
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject* obj);
125
126 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
127
cdaed652
VZ
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
129
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
ce00f59b 131
cdaed652
VZ
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
133
8db2e3ef 134 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
cdaed652 135
8db2e3ef 136 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
ce00f59b 137
cdaed652
VZ
138private:
139 wxRichTextFloatRectMapArray m_left;
140 wxRichTextFloatRectMapArray m_right;
603f702b
JS
141 //int m_width;
142 wxRect m_availableRect;
cdaed652
VZ
143 wxRichTextParagraph* m_para;
144};
145
07d4142f
JS
146// Delete a float
147bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
148{
149 size_t i;
150 for (i = 0; i < m_left.GetCount(); i++)
151 {
152 if (m_left[i]->anchor == obj)
153 {
154 m_left.RemoveAt(i);
155 return true;
156 }
157 }
158 for (i = 0; i < m_right.GetCount(); i++)
159 {
160 if (m_right[i]->anchor == obj)
161 {
162 m_right.RemoveAt(i);
163 return true;
164 }
165 }
166 return false;
167}
168
169// Do we have this float already?
170bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
171{
172 size_t i;
173 for (i = 0; i < m_left.GetCount(); i++)
174 {
175 if (m_left[i]->anchor == obj)
176 {
177 return true;
178 }
179 }
180 for (i = 0; i < m_right.GetCount(); i++)
181 {
182 if (m_right[i]->anchor == obj)
183 {
184 return true;
185 }
186 }
187 return false;
188}
189
603f702b
JS
190// Get floating objects
191bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
192{
193 size_t i;
194 for (i = 0; i < m_left.GetCount(); i++)
195 objects.Append(m_left[i]->anchor);
196 for (i = 0; i < m_right.GetCount(); i++)
197 objects.Append(m_right[i]->anchor);
198 return true;
199}
200
201
cdaed652
VZ
202/*
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
207 */
208int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
209{
210 int end = array.GetCount() - 1;
211 int start = 0;
212 int ret = 0;
213
214 wxASSERT(end >= 0);
215
216 while (true)
217 {
218 if (start > end)
219 {
220 break;
221 }
222
223 int mid = (start + end) / 2;
224 if (array[mid]->startY <= point && array[mid]->endY >= point)
225 return mid;
226 else if (array[mid]->startY > point)
227 {
228 end = mid - 1;
229 ret = mid;
230 }
231 else if (array[mid]->endY < point)
232 {
233 start = mid + 1;
234 ret = start;
235 }
236 }
237
238 return ret;
239}
240
241int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
242{
243 int ret = 0;
244 int len = array.GetCount();
245
246 wxASSERT(index >= 0 && index < len);
247
248 if (array[index]->startY < startY && array[index]->endY > startY)
249 ret = ret < array[index]->width ? array[index]->width : ret;
250 while (index < len && array[index]->startY <= endY)
251 {
252 ret = ret < array[index]->width ? array[index]->width : ret;
253 index++;
254 }
255
256 return ret;
257}
258
603f702b 259wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
cdaed652 260{
603f702b 261 m_availableRect = rect;
cdaed652
VZ
262 m_para = NULL;
263}
264
265void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
266{
267 int len = array.GetCount();
268 for (int i = 0; i < len; i++)
269 delete array[i];
270}
271
272wxRichTextFloatCollector::~wxRichTextFloatCollector()
273{
274 FreeFloatRectMapArray(m_left);
275 FreeFloatRectMapArray(m_right);
276}
277
278int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
279{
280 if (array.GetCount() == 0)
281 return start;
282
603f702b 283 int i = SearchAdjacentRect(array, start);
cdaed652 284 int last = start;
603f702b 285 while (i < (int) array.GetCount())
cdaed652
VZ
286 {
287 if (array[i]->startY - last >= height)
288 return last + 1;
289 last = array[i]->endY;
290 i++;
291 }
292
293 return last + 1;
294}
295
296int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
297{
24777478 298 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
cdaed652 299 return GetFitPosition(m_left, start, height);
24777478 300 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
cdaed652
VZ
301 return GetFitPosition(m_right, start, height);
302 else
303 {
304 wxASSERT("Never should be here");
305 return start;
306 }
307}
308
603f702b
JS
309// Adds a floating image to the float collector.
310// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
cdaed652
VZ
311void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
312{
603f702b 313 int direction = floating->GetFloatDirection();
24777478 314
603f702b
JS
315 wxPoint pos = floating->GetPosition();
316 wxSize size = floating->GetCachedSize();
317 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
318 switch (direction)
319 {
320 case wxTEXT_BOX_ATTR_FLOAT_NONE:
321 delete map;
322 break;
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
326 m_left.Add(map);
327 break;
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
330 m_right.Add(map);
331 break;
332 default:
333 delete map;
334 wxASSERT("Unrecognised float attribute.");
335 }
cdaed652 336
603f702b 337 m_para = para;
cdaed652
VZ
338}
339
340void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
341{
342 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
343 while (node)
344 {
345 wxRichTextObject* floating = node->GetData();
ce00f59b 346
cdaed652
VZ
347 if (floating->IsFloating())
348 {
bec80f4f 349 CollectFloat(para, floating);
cdaed652 350 }
ce00f59b 351
cdaed652
VZ
352 node = node->GetNext();
353 }
354
355 m_para = para;
356}
357
358wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
359{
360 return m_para;
361}
362
363wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
364{
365 int widthLeft = 0, widthRight = 0;
366 if (m_left.GetCount() != 0)
367 {
603f702b
JS
368 int i = SearchAdjacentRect(m_left, startY);
369 if (i < (int) m_left.GetCount())
cdaed652
VZ
370 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
371 }
372 if (m_right.GetCount() != 0)
373 {
603f702b
JS
374 int j = SearchAdjacentRect(m_right, startY);
375 if (j < (int) m_right.GetCount())
cdaed652
VZ
376 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
377 }
378
603f702b
JS
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
cdaed652
VZ
383}
384
385int wxRichTextFloatCollector::GetLastRectBottom()
386{
387 int ret = 0;
388 int len = m_left.GetCount();
389 if (len) {
390 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
391 }
392 len = m_right.GetCount();
393 if (len) {
394 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
395 }
396
397 return ret;
398}
399
8db2e3ef 400void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
401{
402 int start = rect.y;
403 int end = rect.y + rect.height;
603f702b 404 int i, j;
cdaed652 405 i = SearchAdjacentRect(array, start);
603f702b 406 if (i < 0 || i >= (int) array.GetCount())
cdaed652
VZ
407 return;
408 j = SearchAdjacentRect(array, end);
603f702b 409 if (j < 0 || j >= (int) array.GetCount())
cdaed652
VZ
410 j = array.GetCount() - 1;
411 while (i <= j)
412 {
413 wxRichTextObject* obj = array[i]->anchor;
414 wxRichTextRange r = obj->GetRange();
8db2e3ef 415 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
cdaed652
VZ
416 i++;
417 }
418}
ecb5fbf1 419
8db2e3ef 420void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
421{
422 if (m_left.GetCount() > 0)
8db2e3ef 423 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
cdaed652 424 if (m_right.GetCount() > 0)
8db2e3ef 425 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
cdaed652
VZ
426}
427
8db2e3ef 428int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
cdaed652 429{
603f702b 430 int i;
cdaed652
VZ
431 if (array.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE;
433 i = SearchAdjacentRect(array, pt.y);
603f702b 434 if (i < 0 || i >= (int) array.GetCount())
cdaed652 435 return wxRICHTEXT_HITTEST_NONE;
603f702b
JS
436 if (!array[i]->anchor->IsShown())
437 return wxRICHTEXT_HITTEST_NONE;
438
cdaed652
VZ
439 wxPoint point = array[i]->anchor->GetPosition();
440 wxSize size = array[i]->anchor->GetCachedSize();
441 if (point.x <= pt.x && point.x + size.x >= pt.x
442 && point.y <= pt.y && point.y + size.y >= pt.y)
443 {
444 textPosition = array[i]->anchor->GetRange().GetStart();
603f702b 445 * obj = array[i]->anchor;
cdaed652
VZ
446 if (pt.x > (pt.x + pt.x + size.x) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE;
448 else
449 return wxRICHTEXT_HITTEST_AFTER;
450 }
ce00f59b 451
cdaed652
VZ
452 return wxRICHTEXT_HITTEST_NONE;
453}
454
8db2e3ef 455int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
cdaed652 456{
8db2e3ef 457 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
458 if (ret == wxRICHTEXT_HITTEST_NONE)
459 {
8db2e3ef 460 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
cdaed652
VZ
461 }
462 return ret;
463}
464
ce00f59b 465// Helpers for efficiency
ecb5fbf1
JS
466inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
467{
ecb5fbf1
JS
468 dc.SetFont(font);
469}
470
471inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
472{
473 const wxPen& pen1 = dc.GetPen();
474 if (pen1.IsOk() && pen.IsOk())
475 {
476 if (pen1.GetWidth() == pen.GetWidth() &&
477 pen1.GetStyle() == pen.GetStyle() &&
478 pen1.GetColour() == pen.GetColour())
479 return;
480 }
481 dc.SetPen(pen);
482}
483
484inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
485{
486 const wxBrush& brush1 = dc.GetBrush();
487 if (brush1.IsOk() && brush.IsOk())
488 {
489 if (brush1.GetStyle() == brush.GetStyle() &&
490 brush1.GetColour() == brush.GetColour())
491 return;
492 }
493 dc.SetBrush(brush);
494}
495
5d7836c4
JS
496/*!
497 * wxRichTextObject
498 * This is the base for drawable objects.
499 */
500
501IMPLEMENT_CLASS(wxRichTextObject, wxObject)
502
503wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
504{
5d7836c4
JS
505 m_refCount = 1;
506 m_parent = parent;
5d7836c4 507 m_descent = 0;
603f702b 508 m_show = true;
5d7836c4
JS
509}
510
511wxRichTextObject::~wxRichTextObject()
512{
513}
514
515void wxRichTextObject::Dereference()
516{
517 m_refCount --;
518 if (m_refCount <= 0)
519 delete this;
520}
521
522/// Copy
523void wxRichTextObject::Copy(const wxRichTextObject& obj)
524{
525 m_size = obj.m_size;
603f702b
JS
526 m_maxSize = obj.m_maxSize;
527 m_minSize = obj.m_minSize;
5d7836c4 528 m_pos = obj.m_pos;
5d7836c4 529 m_range = obj.m_range;
603f702b 530 m_ownRange = obj.m_ownRange;
5d7836c4 531 m_attributes = obj.m_attributes;
bec80f4f 532 m_properties = obj.m_properties;
5d7836c4 533 m_descent = obj.m_descent;
603f702b
JS
534 m_show = obj.m_show;
535}
536
537// Get/set the top-level container of this object.
538wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
539{
540 const wxRichTextObject* p = this;
541 while (p)
542 {
543 if (p->IsTopLevel())
544 {
545 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
546 }
547 p = p->GetParent();
548 }
549 return NULL;
5d7836c4
JS
550}
551
552void wxRichTextObject::SetMargins(int margin)
553{
603f702b 554 SetMargins(margin, margin, margin, margin);
5d7836c4
JS
555}
556
557void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
558{
603f702b
JS
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
563}
564
565int wxRichTextObject::GetLeftMargin() const
566{
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
568}
569
570int wxRichTextObject::GetRightMargin() const
571{
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
573}
574
575int wxRichTextObject::GetTopMargin() const
576{
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
578}
579
580int wxRichTextObject::GetBottomMargin() const
581{
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
583}
584
585// Calculate the available content space in the given rectangle, given the
586// margins, border and padding specified in the object's attributes.
8db2e3ef 587wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
603f702b
JS
588{
589 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590 marginRect = outerRect;
8db2e3ef
JS
591 wxRichTextAttr attr(GetAttributes());
592 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
594 return contentRect;
595}
596
597// Invalidate the buffer. With no argument, invalidates whole buffer.
598void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
599{
600 if (invalidRange != wxRICHTEXT_NONE)
601 {
23698b12
JS
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating())
606 SetCachedSize(wxDefaultSize);
603f702b
JS
607 SetMaxSize(wxDefaultSize);
608 SetMinSize(wxDefaultSize);
609 }
5d7836c4
JS
610}
611
44219ff0 612// Convert units in tenths of a millimetre to device units
cdaed652 613int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
5d7836c4 614{
44219ff0 615 // Unscale
bec80f4f
JS
616 double scale = 1.0;
617 if (GetBuffer())
32423dd8 618 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
bec80f4f
JS
619 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
620
44219ff0
JS
621 return p;
622}
623
624// Convert units in tenths of a millimetre to device units
bec80f4f 625int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
44219ff0 626{
5d7836c4
JS
627 // There are ppi pixels in 254.1 "1/10 mm"
628
629 double pixels = ((double) units * (double)ppi) / 254.1;
bec80f4f
JS
630 if (scale != 1.0)
631 pixels /= scale;
5d7836c4 632
603f702b
JS
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels == 0 && units > 0)
635 pixels = 1;
636
5d7836c4
JS
637 return (int) pixels;
638}
639
24777478
JS
640// Convert units in pixels to tenths of a millimetre
641int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
642{
643 int p = pixels;
bec80f4f
JS
644 double scale = 1.0;
645 if (GetBuffer())
646 scale = GetBuffer()->GetScale();
647
648 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
24777478
JS
649}
650
bec80f4f 651int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
24777478
JS
652{
653 // There are ppi pixels in 254.1 "1/10 mm"
bec80f4f
JS
654
655 double p = double(pixels);
656
657 if (scale != 1.0)
658 p *= scale;
659
660 int units = int( p * 254.1 / (double) ppi );
24777478
JS
661 return units;
662}
663
bec80f4f 664// Draw the borders and background for the given rectangle and attributes.
603f702b
JS
665// Width and height are taken to be the outer margin size, not the content.
666bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
bec80f4f
JS
667{
668 // Assume boxRect is the area around the content
603f702b
JS
669 wxRect marginRect = boxRect;
670 wxRect contentRect, borderRect, paddingRect, outlineRect;
bec80f4f 671
603f702b 672 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
bec80f4f
JS
673
674 // Margin is transparent. Draw background from margin.
603f702b 675 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
bec80f4f 676 {
603f702b
JS
677 wxColour colour;
678 if (flags & wxRICHTEXT_DRAW_SELECTED)
679 {
680 // TODO: get selection colour from control?
681 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
682 }
683 else
684 colour = attr.GetBackgroundColour();
685
686 wxPen pen(colour);
687 wxBrush brush(colour);
bec80f4f
JS
688
689 dc.SetPen(pen);
690 dc.SetBrush(brush);
37e7b783 691 dc.DrawRectangle(borderRect);
bec80f4f
JS
692 }
693
603f702b
JS
694 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
695 {
696 wxRichTextAttr editBorderAttr = attr;
697 // TODO: make guideline colour configurable
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
701
702 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
703 }
704
705 if (attr.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
bec80f4f 707
603f702b
JS
708 if (attr.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
bec80f4f
JS
710
711 return true;
712}
713
714// Draw a border
603f702b 715bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
bec80f4f
JS
716{
717 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
603f702b 718 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 719
603f702b 720 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
721 {
722 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723 wxColour col(attr.GetLeft().GetColour());
724
725 // If pen width is > 1, resorts to a solid rectangle.
726 if (borderLeft == 1)
727 {
728 int penStyle = wxSOLID;
729 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
730 penStyle = wxDOT;
731 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732 penStyle = wxLONG_DASH;
603f702b 733 wxPen pen(col, 1, penStyle);
bec80f4f
JS
734 dc.SetPen(pen);
735 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
736
737 }
738 else if (borderLeft > 1)
739 {
740 wxPen pen(col);
741 wxBrush brush(col);
742 dc.SetPen(pen);
743 dc.SetBrush(brush);
744 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
745 }
746 }
747
603f702b 748 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
749 {
750 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
751
752 wxColour col(attr.GetRight().GetColour());
753
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight == 1)
756 {
757 int penStyle = wxSOLID;
758 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
759 penStyle = wxDOT;
760 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761 penStyle = wxLONG_DASH;
603f702b 762 wxPen pen(col, 1, penStyle);
bec80f4f 763 dc.SetPen(pen);
603f702b 764 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
bec80f4f
JS
765
766 }
767 else if (borderRight > 1)
768 {
769 wxPen pen(col);
770 wxBrush brush(col);
771 dc.SetPen(pen);
772 dc.SetBrush(brush);
63af79de 773 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
bec80f4f
JS
774 }
775 }
776
603f702b 777 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
778 {
779 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
780
781 wxColour col(attr.GetTop().GetColour());
782
783 // If pen width is > 1, resorts to a solid rectangle.
784 if (borderTop == 1)
785 {
786 int penStyle = wxSOLID;
787 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
788 penStyle = wxDOT;
789 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790 penStyle = wxLONG_DASH;
603f702b 791 wxPen pen(col, 1, penStyle);
bec80f4f
JS
792 dc.SetPen(pen);
793 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
794
795 }
796 else if (borderTop > 1)
797 {
798 wxPen pen(col);
799 wxBrush brush(col);
800 dc.SetPen(pen);
801 dc.SetBrush(brush);
802 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
803 }
804 }
805
603f702b 806 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
bec80f4f
JS
807 {
808 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
809 wxColour col(attr.GetTop().GetColour());
810
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom == 1)
813 {
814 int penStyle = wxSOLID;
815 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
816 penStyle = wxDOT;
817 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818 penStyle = wxLONG_DASH;
603f702b 819 wxPen pen(col, 1, penStyle);
bec80f4f
JS
820 dc.SetPen(pen);
821 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
822
823 }
824 else if (borderBottom > 1)
825 {
826 wxPen pen(col);
827 wxBrush brush(col);
828 dc.SetPen(pen);
829 dc.SetBrush(brush);
63af79de 830 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
bec80f4f
JS
831 }
832 }
833
834 return true;
835}
836
837// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838// or marginRect (outer), and the other must be the default rectangle (no width or height).
839// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
840// is available.
841//
842// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843
603f702b 844bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
bec80f4f
JS
845{
846 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
850
603f702b 851 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
bec80f4f 852
603f702b 853 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
bec80f4f 854 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
603f702b 855 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
bec80f4f 856 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
603f702b 857 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
bec80f4f 858 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
83c6ae8e 859 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
bec80f4f
JS
860 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
861
603f702b 862 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
bec80f4f 863 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
603f702b 864 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
bec80f4f 865 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
603f702b 866 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
bec80f4f 867 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
83c6ae8e 868 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
bec80f4f
JS
869 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870
603f702b 871 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
bec80f4f 872 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
603f702b 873 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
bec80f4f 874 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
603f702b 875 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
bec80f4f 876 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b 877 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
bec80f4f
JS
878 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
879
603f702b 880 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
bec80f4f 881 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
603f702b 882 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
bec80f4f 883 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
603f702b 884 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
bec80f4f 885 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
603f702b 886 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
bec80f4f
JS
887 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888
889 int leftTotal = marginLeft + borderLeft + paddingLeft;
890 int rightTotal = marginRight + borderRight + paddingRight;
891 int topTotal = marginTop + borderTop + paddingTop;
892 int bottomTotal = marginBottom + borderBottom + paddingBottom;
893
894 if (marginRect != wxRect())
895 {
896 contentRect.x = marginRect.x + leftTotal;
897 contentRect.y = marginRect.y + topTotal;
898 contentRect.width = marginRect.width - (leftTotal + rightTotal);
899 contentRect.height = marginRect.height - (topTotal + bottomTotal);
900 }
901 else
902 {
903 marginRect.x = contentRect.x - leftTotal;
904 marginRect.y = contentRect.y - topTotal;
905 marginRect.width = contentRect.width + (leftTotal + rightTotal);
906 marginRect.height = contentRect.height + (topTotal + bottomTotal);
907 }
908
909 borderRect.x = marginRect.x + marginLeft;
910 borderRect.y = marginRect.y + marginTop;
911 borderRect.width = marginRect.width - (marginLeft + marginRight);
912 borderRect.height = marginRect.height - (marginTop + marginBottom);
913
914 paddingRect.x = marginRect.x + marginLeft + borderLeft;
915 paddingRect.y = marginRect.y + marginTop + borderTop;
916 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
918
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect.x = marginRect.x - outlineLeft;
921 outlineRect.y = marginRect.y - outlineTop;
922 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
924
925 return true;
926}
927
603f702b
JS
928// Get the total margin for the object in pixels, taking into account margin, padding and border size
929bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930 int& topMargin, int& bottomMargin)
931{
932 // Assume boxRect is the area around the content
933 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934 marginRect = wxRect(0, 0, 1000, 1000);
bec80f4f 935
603f702b
JS
936 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
937
938 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939 rightMargin = marginRect.GetRight() - contentRect.GetRight();
940 topMargin = contentRect.GetTop() - marginRect.GetTop();
941 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
942
943 return true;
944}
945
946// Returns the rectangle which the child has available to it given restrictions specified in the
947// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
bb7bbd12
JS
948// availableContainerSpace might be a parent that the cell has to compute its width relative to.
949// E.g. a cell that's 50% of its parent.
950wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
603f702b
JS
951{
952 wxRect rect = availableParentSpace;
953 double scale = 1.0;
954 if (buffer)
955 scale = buffer->GetScale();
956
bb7bbd12 957 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
603f702b
JS
958
959 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
961
962 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
964
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 {
969 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
970 }
971 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 {
973 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 975 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
603f702b
JS
976 else
977 rect.x += x;
978 }
979
980 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 {
982 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
983 }
984 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 {
986 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
bb7bbd12 988 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
603f702b
JS
989 else
990 rect.y += y;
991 }
992
bb7bbd12
JS
993 if (rect.GetWidth() > availableParentSpace.GetWidth())
994 rect.SetWidth(availableParentSpace.GetWidth());
995
603f702b
JS
996 return rect;
997}
998
999// Dump to output stream for debugging
5d7836c4
JS
1000void wxRichTextObject::Dump(wxTextOutputStream& stream)
1001{
1002 stream << GetClassInfo()->GetClassName() << wxT("\n");
1003 stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
1004 stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
1005}
1006
603f702b 1007// Gets the containing buffer
44219ff0
JS
1008wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1009{
1010 const wxRichTextObject* obj = this;
345c78ca 1011 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
44219ff0
JS
1012 obj = obj->GetParent();
1013 return wxDynamicCast(obj, wxRichTextBuffer);
1014}
5d7836c4 1015
603f702b
JS
1016// Get the absolute object position, by traversing up the child/parent hierarchy
1017wxPoint wxRichTextObject::GetAbsolutePosition() const
1018{
1019 wxPoint pt = GetPosition();
1020
1021 wxRichTextObject* p = GetParent();
1022 while (p)
1023 {
1024 pt = pt + p->GetPosition();
1025 p = p->GetParent();
1026 }
1027
1028 return pt;
1029}
1030
1031// Hit-testing: returns a flag indicating hit test details, plus
1032// information about position
8db2e3ef 1033int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
603f702b
JS
1034{
1035 if (!IsShown())
1036 return wxRICHTEXT_HITTEST_NONE;
1037
1038 wxRect rect = GetRect();
1039 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040 pt.y >= rect.y && pt.y < rect.y + rect.height)
1041 {
1042 *obj = this;
1043 *contextObj = GetParentContainer();
1044 textPosition = GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON;
1046 }
1047 else
1048 return wxRICHTEXT_HITTEST_NONE;
1049}
1050
1051// Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052// lays out the object again using the maximum ('best') size
8db2e3ef 1053bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
bb7bbd12
JS
1054 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
603f702b
JS
1056 int style)
1057{
bb7bbd12 1058 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
603f702b 1059 wxRect originalAvailableRect = availableChildRect;
8db2e3ef 1060 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1061
1062 wxSize maxSize = GetMaxSize();
1063
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 // on this basis
bb7bbd12 1066 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
603f702b
JS
1067 {
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL);
1070 wxRichTextAttr newAttr(attr);
1071 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1073
bb7bbd12 1074 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
603f702b
JS
1075
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
07d4142f 1079 if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
603f702b
JS
1080 {
1081 // centering, right-justification
8db2e3ef 1082 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
1083 {
1084 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1085 }
8db2e3ef 1086 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
1087 {
1088 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1089 }
1090 }
1091
8db2e3ef 1092 Layout(dc, context, availableChildRect, availableContainerSpace, style);
603f702b
JS
1093 }
1094
1095 /*
1096 __________________
1097 | ____________ |
1098 | | | |
1099
1100
1101 */
1102
1103 return true;
1104}
1105
1106// Move the object recursively, by adding the offset from old to new
1107void wxRichTextObject::Move(const wxPoint& pt)
1108{
1109 SetPosition(pt);
1110}
1111
1112
5d7836c4
JS
1113/*!
1114 * wxRichTextCompositeObject
1115 * This is the base for drawable objects.
1116 */
1117
1118IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1119
1120wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1121 wxRichTextObject(parent)
1122{
1123}
1124
1125wxRichTextCompositeObject::~wxRichTextCompositeObject()
1126{
1127 DeleteChildren();
1128}
1129
1130/// Get the nth child
1131wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1132{
1133 wxASSERT ( n < m_children.GetCount() );
1134
1135 return m_children.Item(n)->GetData();
1136}
1137
1138/// Append a child, returning the position
1139size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1140{
1141 m_children.Append(child);
1142 child->SetParent(this);
1143 return m_children.GetCount() - 1;
1144}
1145
1146/// Insert the child in front of the given object, or at the beginning
1147bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1148{
1149 if (inFrontOf)
1150 {
1151 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1152 m_children.Insert(node, child);
1153 }
1154 else
1155 m_children.Insert(child);
1156 child->SetParent(this);
1157
1158 return true;
1159}
1160
1161/// Delete the child
1162bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1163{
1164 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1165 if (node)
1166 {
efbf6735
JS
1167 wxRichTextObject* obj = node->GetData();
1168 m_children.Erase(node);
5d7836c4 1169 if (deleteChild)
efbf6735 1170 delete obj;
5d7836c4
JS
1171
1172 return true;
1173 }
1174 return false;
1175}
1176
1177/// Delete all children
1178bool wxRichTextCompositeObject::DeleteChildren()
1179{
1180 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1181 while (node)
1182 {
1183 wxRichTextObjectList::compatibility_iterator oldNode = node;
1184
1185 wxRichTextObject* child = node->GetData();
1186 child->Dereference(); // Only delete if reference count is zero
1187
1188 node = node->GetNext();
efbf6735 1189 m_children.Erase(oldNode);
5d7836c4
JS
1190 }
1191
1192 return true;
1193}
1194
1195/// Get the child count
1196size_t wxRichTextCompositeObject::GetChildCount() const
1197{
1198 return m_children.GetCount();
1199}
1200
1201/// Copy
1202void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1203{
1204 wxRichTextObject::Copy(obj);
1205
1206 DeleteChildren();
1207
1208 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1209 while (node)
1210 {
1211 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
1212 wxRichTextObject* newChild = child->Clone();
1213 newChild->SetParent(this);
1214 m_children.Append(newChild);
5d7836c4
JS
1215
1216 node = node->GetNext();
1217 }
1218}
1219
1220/// Hit-testing: returns a flag indicating hit test details, plus
1221/// information about position
8db2e3ef 1222int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 1223{
603f702b
JS
1224 if (!IsShown())
1225 return wxRICHTEXT_HITTEST_NONE;
1226
5d7836c4
JS
1227 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1228 while (node)
1229 {
1230 wxRichTextObject* child = node->GetData();
1231
603f702b
JS
1232 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1233 {
1234 // Just check if we hit the overall object
8db2e3ef 1235 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1236 if (ret != wxRICHTEXT_HITTEST_NONE)
1237 return ret;
1238 }
1239 else if (child->IsShown())
1240 {
8db2e3ef 1241 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
1242 if (ret != wxRICHTEXT_HITTEST_NONE)
1243 return ret;
1244 }
5d7836c4
JS
1245
1246 node = node->GetNext();
1247 }
1248
603f702b 1249 return wxRICHTEXT_HITTEST_NONE;
5d7836c4
JS
1250}
1251
1252/// Finds the absolute position and row height for the given character position
8db2e3ef 1253bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
1254{
1255 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1256 while (node)
1257 {
1258 wxRichTextObject* child = node->GetData();
1259
603f702b
JS
1260 // Don't recurse if the child is a top-level object,
1261 // such as a text box, because the character position will no longer
1262 // apply. By definition, a top-level object has its own range of
1263 // character positions.
8db2e3ef 1264 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
5d7836c4
JS
1265 return true;
1266
1267 node = node->GetNext();
1268 }
1269
1270 return false;
1271}
1272
1273/// Calculate range
1274void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1275{
1276 long current = start;
1277 long lastEnd = current;
1278
603f702b
JS
1279 if (IsTopLevel())
1280 {
1281 current = 0;
1282 lastEnd = 0;
1283 }
1284
5d7836c4
JS
1285 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1286 while (node)
1287 {
1288 wxRichTextObject* child = node->GetData();
1289 long childEnd = 0;
1290
1291 child->CalculateRange(current, childEnd);
1292 lastEnd = childEnd;
1293
1294 current = childEnd + 1;
1295
1296 node = node->GetNext();
1297 }
1298
603f702b
JS
1299 if (IsTopLevel())
1300 {
1301 // A top-level object always has a range of size 1,
1302 // because its children don't count at this level.
1303 end = start;
1304 m_range.SetRange(start, start);
5d7836c4 1305
603f702b
JS
1306 // An object with no children has zero length
1307 if (m_children.GetCount() == 0)
1308 lastEnd --;
1309 m_ownRange.SetRange(0, lastEnd);
1310 }
1311 else
1312 {
1313 end = lastEnd;
5d7836c4 1314
603f702b
JS
1315 // An object with no children has zero length
1316 if (m_children.GetCount() == 0)
1317 end --;
1318
1319 m_range.SetRange(start, end);
1320 }
5d7836c4
JS
1321}
1322
1323/// Delete range from layout.
1324bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1325{
1326 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1327
5d7836c4
JS
1328 while (node)
1329 {
1330 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1331 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1332
5d7836c4
JS
1333 // Delete the range in each paragraph
1334
1335 // When a chunk has been deleted, internally the content does not
1336 // now match the ranges.
1337 // However, so long as deletion is not done on the same object twice this is OK.
1338 // If you may delete content from the same object twice, recalculate
1339 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1340 // adjust the range you're deleting accordingly.
7fe8059f 1341
5d7836c4
JS
1342 if (!obj->GetRange().IsOutside(range))
1343 {
603f702b
JS
1344 // No need to delete within a top-level object; just removing this object will do fine
1345 if (!obj->IsTopLevel())
1346 obj->DeleteRange(range);
5d7836c4
JS
1347
1348 // Delete an empty object, or paragraph within this range.
1349 if (obj->IsEmpty() ||
1350 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1351 {
1352 // An empty paragraph has length 1, so won't be deleted unless the
1353 // whole range is deleted.
7fe8059f 1354 RemoveChild(obj, true);
5d7836c4
JS
1355 }
1356 }
7fe8059f 1357
5d7836c4
JS
1358 node = next;
1359 }
7fe8059f 1360
5d7836c4
JS
1361 return true;
1362}
1363
1364/// Get any text in this object for the given range
1365wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1366{
1367 wxString text;
1368 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1369 while (node)
1370 {
1371 wxRichTextObject* child = node->GetData();
1372 wxRichTextRange childRange = range;
1373 if (!child->GetRange().IsOutside(range))
1374 {
1375 childRange.LimitTo(child->GetRange());
7fe8059f 1376
5d7836c4 1377 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1378
5d7836c4
JS
1379 text += childText;
1380 }
1381 node = node->GetNext();
1382 }
1383
1384 return text;
1385}
1386
603f702b
JS
1387/// Get the child object at the given character position
1388wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1389{
1390 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1391 while (node)
1392 {
1393 wxRichTextObject* child = node->GetData();
1394 if (child->GetRange().GetStart() == pos)
1395 return child;
1396 node = node->GetNext();
1397 }
1398 return NULL;
1399}
1400
5d7836c4 1401/// Recursively merge all pieces that can be merged.
109bfc88 1402bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
5d7836c4
JS
1403{
1404 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1405 while (node)
1406 {
1407 wxRichTextObject* child = node->GetData();
5cb0b827 1408 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
5d7836c4 1409 {
109bfc88
JS
1410 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1411 if (composite)
1412 composite->Defragment();
1413
1414 if (node->GetNext())
5d7836c4 1415 {
109bfc88
JS
1416 wxRichTextObject* nextChild = node->GetNext()->GetData();
1417 if (child->CanMerge(nextChild) && child->Merge(nextChild))
1418 {
1419 nextChild->Dereference();
1420 m_children.Erase(node->GetNext());
5d7836c4 1421
109bfc88
JS
1422 // Don't set node -- we'll see if we can merge again with the next
1423 // child.
1424 }
1425 else
1426 node = node->GetNext();
5d7836c4
JS
1427 }
1428 else
1429 node = node->GetNext();
1430 }
1431 else
1432 node = node->GetNext();
1433 }
1434
bec80f4f
JS
1435 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1436 if (GetChildCount() > 1)
5d7836c4 1437 {
bec80f4f
JS
1438 node = m_children.GetFirst();
1439 while (node)
1440 {
1441 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1442 wxRichTextObject* child = node->GetData();
1443 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1444 {
1445 if (child->IsEmpty())
1446 {
1447 child->Dereference();
1448 m_children.Erase(node);
1449 }
1450 node = next;
1451 }
1452 else
1453 node = node->GetNext();
1454 }
5d7836c4 1455 }
5d7836c4 1456
5d7836c4
JS
1457 return true;
1458}
1459
bec80f4f
JS
1460/// Dump to output stream for debugging
1461void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
5d7836c4
JS
1462{
1463 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1464 while (node)
1465 {
1466 wxRichTextObject* child = node->GetData();
bec80f4f 1467 child->Dump(stream);
5d7836c4
JS
1468 node = node->GetNext();
1469 }
5d7836c4
JS
1470}
1471
603f702b
JS
1472/// Get/set the object size for the given range. Returns false if the range
1473/// is invalid for this object.
8db2e3ef 1474bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b
JS
1475{
1476 if (!range.IsWithin(GetRange()))
1477 return false;
5d7836c4 1478
603f702b 1479 wxSize sz;
5d7836c4 1480
603f702b
JS
1481 wxArrayInt childExtents;
1482 wxArrayInt* p;
1483 if (partialExtents)
1484 p = & childExtents;
1485 else
1486 p = NULL;
5d7836c4 1487
603f702b
JS
1488 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1489 while (node)
cdaed652 1490 {
603f702b
JS
1491 wxRichTextObject* child = node->GetData();
1492 if (!child->GetRange().IsOutside(range))
1493 {
1494 // Floating objects have a zero size within the paragraph.
1495 if (child->IsFloating())
1496 {
1497 if (partialExtents)
1498 {
1499 int lastSize;
1500 if (partialExtents->GetCount() > 0)
1501 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1502 else
1503 lastSize = 0;
cdaed652 1504
603f702b
JS
1505 partialExtents->Add(0 /* zero size */ + lastSize);
1506 }
1507 }
1508 else
1509 {
1510 wxSize childSize;
5d7836c4 1511
603f702b
JS
1512 wxRichTextRange rangeToUse = range;
1513 rangeToUse.LimitTo(child->GetRange());
1514 if (child->IsTopLevel())
1515 rangeToUse = child->GetOwnRange();
5d7836c4 1516
603f702b 1517 int childDescent = 0;
cdaed652 1518
603f702b
JS
1519 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1520 // but it's only going to be used after caching has taken place.
1521 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1522 {
1523 childDescent = child->GetDescent();
1524 childSize = child->GetCachedSize();
bec80f4f 1525
603f702b
JS
1526 sz.y = wxMax(sz.y, childSize.y);
1527 sz.x += childSize.x;
1528 descent = wxMax(descent, childDescent);
1529 }
8db2e3ef 1530 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b
JS
1531 {
1532 sz.y = wxMax(sz.y, childSize.y);
1533 sz.x += childSize.x;
1534 descent = wxMax(descent, childDescent);
bec80f4f 1535
603f702b
JS
1536 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1537 {
1538 child->SetCachedSize(childSize);
1539 child->SetDescent(childDescent);
1540 }
bec80f4f 1541
603f702b
JS
1542 if (partialExtents)
1543 {
1544 int lastSize;
1545 if (partialExtents->GetCount() > 0)
1546 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1547 else
1548 lastSize = 0;
bec80f4f 1549
603f702b
JS
1550 size_t i;
1551 for (i = 0; i < childExtents.GetCount(); i++)
1552 {
1553 partialExtents->Add(childExtents[i] + lastSize);
1554 }
1555 }
1556 }
1557 }
1558
1559 if (p)
1560 p->Clear();
1561 }
1562
1563 node = node->GetNext();
1564 }
1565 size = sz;
1566 return true;
1567}
1568
1569// Invalidate the buffer. With no argument, invalidates whole buffer.
1570void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1571{
1572 wxRichTextObject::Invalidate(invalidRange);
1573
1574 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1575 while (node)
1576 {
1577 wxRichTextObject* child = node->GetData();
1578 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1579 {
1580 // Skip
1581 }
1582 else if (child->IsTopLevel())
1583 {
1584 if (invalidRange == wxRICHTEXT_NONE)
1585 child->Invalidate(wxRICHTEXT_NONE);
1586 else
1587 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1588 }
1589 else
1590 child->Invalidate(invalidRange);
1591 node = node->GetNext();
1592 }
1593}
1594
1595// Move the object recursively, by adding the offset from old to new
1596void wxRichTextCompositeObject::Move(const wxPoint& pt)
1597{
1598 wxPoint oldPos = GetPosition();
1599 SetPosition(pt);
1600 wxPoint offset = pt - oldPos;
1601
1602 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1603 while (node)
1604 {
1605 wxRichTextObject* child = node->GetData();
1606 wxPoint childPos = child->GetPosition() + offset;
1607 child->Move(childPos);
1608 node = node->GetNext();
1609 }
1610}
1611
1612
1613/*!
1614 * wxRichTextParagraphLayoutBox
1615 * This box knows how to lay out paragraphs.
1616 */
1617
1618IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1619
1620wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1621 wxRichTextCompositeObject(parent)
1622{
1623 Init();
1624}
1625
1626wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1627{
1628 if (m_floatCollector)
1629 {
1630 delete m_floatCollector;
1631 m_floatCollector = NULL;
1632 }
1633}
1634
1635/// Initialize the object.
1636void wxRichTextParagraphLayoutBox::Init()
1637{
1638 m_ctrl = NULL;
1639
1640 // For now, assume is the only box and has no initial size.
1641 m_range = wxRichTextRange(0, -1);
1642 m_ownRange = wxRichTextRange(0, -1);
1643
1644 m_invalidRange = wxRICHTEXT_ALL;
1645
603f702b
JS
1646 m_partialParagraph = false;
1647 m_floatCollector = NULL;
1648}
1649
1650void wxRichTextParagraphLayoutBox::Clear()
1651{
1652 DeleteChildren();
1653
1654 if (m_floatCollector)
1655 delete m_floatCollector;
1656 m_floatCollector = NULL;
1657 m_partialParagraph = false;
1658}
1659
1660/// Copy
1661void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1662{
1663 Clear();
1664
1665 wxRichTextCompositeObject::Copy(obj);
1666
1667 m_partialParagraph = obj.m_partialParagraph;
1668 m_defaultAttributes = obj.m_defaultAttributes;
bec80f4f
JS
1669}
1670
07d4142f
JS
1671// Gather information about floating objects; only gather floats for those paragraphs that
1672// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1673// during layout.
603f702b 1674bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
cdaed652
VZ
1675{
1676 if (m_floatCollector != NULL)
1677 delete m_floatCollector;
603f702b 1678 m_floatCollector = new wxRichTextFloatCollector(availableRect);
cdaed652 1679 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
07d4142f
JS
1680 // Only gather floats up to the point we'll start formatting paragraphs.
1681 while (untilObj && node && node->GetData() != untilObj)
cdaed652
VZ
1682 {
1683 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1684 wxASSERT (child != NULL);
1685 if (child)
1686 m_floatCollector->CollectFloat(child);
1687 node = node->GetNext();
1688 }
ce00f59b 1689
cdaed652
VZ
1690 return true;
1691}
1692
603f702b
JS
1693// Returns the style sheet associated with the overall buffer.
1694wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1695{
1696 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1697}
1698
1699// Get the number of floating objects at this level
1700int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1701{
1702 if (m_floatCollector)
1703 return m_floatCollector->GetFloatingObjectCount();
1704 else
1705 return 0;
1706}
1707
1708// Get a list of floating objects
1709bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1710{
1711 if (m_floatCollector)
1712 {
1713 return m_floatCollector->GetFloatingObjects(objects);
1714 }
1715 else
1716 return false;
1717}
1718
1719// Calculate ranges
1720void wxRichTextParagraphLayoutBox::UpdateRanges()
1721{
1722 long start = 0;
1723 if (GetParent())
1724 start = GetRange().GetStart();
1725 long end;
1726 CalculateRange(start, end);
1727}
1728
cdaed652 1729// HitTest
8db2e3ef 1730int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
cdaed652 1731{
603f702b
JS
1732 if (!IsShown())
1733 return wxRICHTEXT_HITTEST_NONE;
1734
cdaed652 1735 int ret = wxRICHTEXT_HITTEST_NONE;
343ef639 1736 if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
8db2e3ef 1737 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
ce00f59b 1738
cdaed652 1739 if (ret == wxRICHTEXT_HITTEST_NONE)
8db2e3ef 1740 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
cdaed652 1741 else
603f702b
JS
1742 {
1743 *contextObj = this;
cdaed652 1744 return ret;
603f702b 1745 }
cdaed652
VZ
1746}
1747
1748/// Draw the floating objects
8db2e3ef 1749void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
cdaed652
VZ
1750{
1751 if (m_floatCollector)
8db2e3ef 1752 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
cdaed652
VZ
1753}
1754
bec80f4f 1755void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
cdaed652
VZ
1756{
1757 if (from == to)
1758 return;
1759
1760 from->RemoveChild(obj);
1761 to->AppendChild(obj);
5d7836c4
JS
1762}
1763
1764/// Draw the item
8db2e3ef 1765bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
5d7836c4 1766{
603f702b
JS
1767 if (!IsShown())
1768 return true;
1769
1770 wxRect thisRect(GetPosition(), GetCachedSize());
1771
8db2e3ef
JS
1772 wxRichTextAttr attr(GetAttributes());
1773 context.ApplyVirtualAttributes(attr, this);
1774
603f702b
JS
1775 int flags = style;
1776 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1777 flags |= wxRICHTEXT_DRAW_SELECTED;
1778
1779 // Don't draw guidelines if at top level
1780 int theseFlags = flags;
1781 if (!GetParent())
1782 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
8db2e3ef 1783 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
603f702b 1784
8db2e3ef 1785 DrawFloats(dc, context, range, selection, rect, descent, style);
5d7836c4
JS
1786 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1787 while (node)
1788 {
603f702b 1789 wxRichTextObject* child = node->GetData();
7fe8059f 1790
5d7836c4
JS
1791 if (child && !child->GetRange().IsOutside(range))
1792 {
1793 wxRect childRect(child->GetPosition(), child->GetCachedSize());
603f702b
JS
1794 wxRichTextRange childRange = range;
1795 if (child->IsTopLevel())
1796 {
1797 childRange = child->GetOwnRange();
1798 }
7fe8059f 1799
ea160b2e
JS
1800 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1801 {
1802 // Stop drawing
1803 break;
1804 }
1805 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
1806 {
1807 // Skip
1808 }
1809 else
8db2e3ef 1810 child->Draw(dc, context, childRange, selection, rect, descent, style);
5d7836c4
JS
1811 }
1812
1813 node = node->GetNext();
1814 }
1815 return true;
1816}
1817
1818/// Lay the item out
8db2e3ef 1819bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 1820{
603f702b
JS
1821 SetPosition(rect.GetPosition());
1822
1823 if (!IsShown())
1824 return true;
1825
4d551ad5
JS
1826 wxRect availableSpace;
1827 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1828
8db2e3ef
JS
1829 wxRichTextAttr attr(GetAttributes());
1830 context.ApplyVirtualAttributes(attr, this);
1831
4d551ad5 1832 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
1833 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1834 // so that during a size, only the visible part will be relaid out, or
1835 // it would take too long causing flicker. As an approximation, we assume that
1836 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
1837 if (formatRect)
1838 {
603f702b 1839 wxRect rect2(0, 0, rect.width, rect.height);
8db2e3ef 1840 availableSpace = GetAvailableContentArea(dc, context, rect2);
4d551ad5
JS
1841
1842 // Invalidate the part of the buffer from the first visible line
1843 // to the end. If other parts of the buffer are currently invalid,
1844 // then they too will be taken into account if they are above
1845 // the visible point.
1846 long startPos = 0;
1847 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1848 if (line)
1849 startPos = line->GetAbsoluteRange().GetStart();
1850
603f702b 1851 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
4d551ad5
JS
1852 }
1853 else
603f702b 1854 {
8db2e3ef 1855 availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
1856 }
1857
d157d142
JS
1858 // Fix the width if we're at the top level
1859 if (!GetParent())
1860 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1861
603f702b 1862 int leftMargin, rightMargin, topMargin, bottomMargin;
8db2e3ef 1863 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
603f702b 1864 topMargin, bottomMargin);
5d7836c4
JS
1865
1866 int maxWidth = 0;
603f702b
JS
1867 int maxHeight = 0;
1868
1869 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1870 int maxMaxWidth = 0;
1871
1872 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1873 int maxMinWidth = 0;
1874
1875 // If we have vertical alignment, we must recalculate everything.
8db2e3ef
JS
1876 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1877 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
7fe8059f 1878
5d7836c4 1879 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 1880
38113684 1881 bool layoutAll = true;
1e967276 1882
38113684
JS
1883 // Get invalid range, rounding to paragraph start/end.
1884 wxRichTextRange invalidRange = GetInvalidRange(true);
1885
4d551ad5 1886 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
1887 return true;
1888
603f702b 1889 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1e967276 1890 layoutAll = true;
38113684 1891 else // If we know what range is affected, start laying out from that point on.
603f702b 1892 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
2c375f42 1893 {
38113684 1894 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
1895 if (firstParagraph)
1896 {
1897 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
1898 wxRichTextObjectList::compatibility_iterator previousNode;
1899 if ( firstNode )
1900 previousNode = firstNode->GetPrevious();
9b4af7b7 1901 if (firstNode)
2c375f42 1902 {
9b4af7b7
JS
1903 if (previousNode)
1904 {
1905 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1906 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1907 }
7fe8059f 1908
2c375f42
JS
1909 // Now we're going to start iterating from the first affected paragraph.
1910 node = firstNode;
1e967276
JS
1911
1912 layoutAll = false;
2c375f42
JS
1913 }
1914 }
1915 }
1916
07d4142f
JS
1917 // Gather information about only those floating objects that will not be formatted,
1918 // after which floats will be gathered per-paragraph during layout.
603f702b 1919 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
cdaed652 1920
4d551ad5
JS
1921 // A way to force speedy rest-of-buffer layout (the 'else' below)
1922 bool forceQuickLayout = false;
39a1c2f2 1923
d3f6b1b5
JS
1924 // First get the size of the paragraphs we won't be laying out
1925 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1926 while (n && n != node)
1927 {
1928 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
1929 if (child)
1930 {
1931 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1932 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1933 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1934 }
1935 n = n->GetNext();
1936 }
1937
5d7836c4
JS
1938 while (node)
1939 {
1940 // Assume this box only contains paragraphs
1941
1942 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
706465df
JS
1943 // Unsure if this is needed
1944 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
7fe8059f 1945
603f702b 1946 if (child && child->IsShown())
2c375f42 1947 {
603f702b
JS
1948 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1949 if ( !forceQuickLayout &&
1950 (layoutAll ||
1951 child->GetLines().IsEmpty() ||
1952 !child->GetRange().IsOutside(invalidRange)) )
1953 {
1954 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1955 // lays out the object again using the minimum size
8db2e3ef
JS
1956 child->LayoutToBestSize(dc, context, GetBuffer(),
1957 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
1958
1959 // Layout must set the cached size
1960 availableSpace.y += child->GetCachedSize().y;
1961 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1962 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1963 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1964
1965 // If we're just formatting the visible part of the buffer,
1966 // and we're now past the bottom of the window, and we don't have any
1967 // floating objects (since they may cause wrapping to change for the rest of the
1968 // the buffer), start quick layout.
1969 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
1970 forceQuickLayout = true;
1971 }
1972 else
1973 {
1974 // We're outside the immediately affected range, so now let's just
1975 // move everything up or down. This assumes that all the children have previously
1976 // been laid out and have wrapped line lists associated with them.
1977 // TODO: check all paragraphs before the affected range.
1978
1979 int inc = availableSpace.y - child->GetPosition().y;
1980
1981 while (node)
1982 {
1983 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1984 if (child)
1985 {
1986 if (child->GetLines().GetCount() == 0)
1987 {
1988 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1989 // lays out the object again using the minimum size
8db2e3ef
JS
1990 child->LayoutToBestSize(dc, context, GetBuffer(),
1991 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
603f702b
JS
1992
1993 //child->Layout(dc, availableChildRect, style);
1994 }
1995 else
1996 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
5d7836c4 1997
603f702b
JS
1998 availableSpace.y += child->GetCachedSize().y;
1999 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2000 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2001 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2002 }
4d551ad5 2003
603f702b
JS
2004 node = node->GetNext();
2005 }
2006 break;
2007 }
2c375f42 2008 }
7fe8059f 2009
603f702b
JS
2010 node = node->GetNext();
2011 }
2012
2013 node = m_children.GetLast();
2014 if (node && node->GetData()->IsShown())
2015 {
2016 wxRichTextObject* child = node->GetData();
603f702b
JS
2017 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2018 }
2019 else
2020 maxHeight = 0; // topMargin + bottomMargin;
2021
23698b12
JS
2022 // Check the bottom edge of any floating object
2023 if (GetFloatCollector() && GetFloatCollector()->HasFloats())
2024 {
2025 int bottom = GetFloatCollector()->GetLastRectBottom();
2026 if (bottom > maxHeight)
2027 maxHeight = bottom;
2028 }
2029
8db2e3ef 2030 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
bb7bbd12 2031 {
8db2e3ef 2032 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
bb7bbd12
JS
2033 int w = r.GetWidth();
2034
2035 // Convert external to content rect
2036 w = w - leftMargin - rightMargin;
2037 maxWidth = wxMax(maxWidth, w);
2038 maxMaxWidth = wxMax(maxMaxWidth, w);
2039 }
32423dd8
JS
2040 else
2041 {
2042 // TODO: Make sure the layout box's position reflects
2043 // the position of the children, but without
2044 // breaking layout of a box within a paragraph.
2045 }
bb7bbd12 2046
603f702b
JS
2047 // TODO: (also in para layout) should set the
2048 // object's size to an absolute one if specified,
2049 // but if not specified, calculate it from content.
2050
2051 // We need to add back the margins etc.
2052 {
2053 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2054 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
8db2e3ef 2055 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2056 SetCachedSize(marginRect.GetSize());
2057 }
2058
2059 // The maximum size is the greatest of all maximum widths for all paragraphs.
2060 {
2061 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2062 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2063 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2064 SetMaxSize(marginRect.GetSize());
2065 }
2066
2067 // The minimum size is the greatest of all minimum widths for all paragraphs.
2068 {
2069 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2070 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
8db2e3ef 2071 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
2072 SetMinSize(marginRect.GetSize());
2073 }
2074
8db2e3ef
JS
2075 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2076 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
603f702b
JS
2077 {
2078 int yOffset = 0;
2079 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2080 if (leftOverSpace > 0)
2081 {
8db2e3ef 2082 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
603f702b
JS
2083 {
2084 yOffset = (leftOverSpace/2);
2085 }
8db2e3ef 2086 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
603f702b
JS
2087 {
2088 yOffset = leftOverSpace;
2089 }
2090 }
7fe8059f 2091
603f702b
JS
2092 // Move all the children to vertically align the content
2093 // This doesn't take into account floating objects, unfortunately.
2094 if (yOffset != 0)
2095 {
2096 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2c375f42
JS
2097 while (node)
2098 {
2099 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2100 if (child)
603f702b 2101 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
7fe8059f
WS
2102
2103 node = node->GetNext();
2c375f42 2104 }
2c375f42 2105 }
5d7836c4
JS
2106 }
2107
1e967276 2108 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
2109
2110 return true;
2111}
2112
5d7836c4 2113/// Get/set the size for the given range.
8db2e3ef 2114bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
5d7836c4
JS
2115{
2116 wxSize sz;
2117
09f14108
JS
2118 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2119 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
2120
2121 // First find the first paragraph whose starting position is within the range.
2122 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2123 while (node)
2124 {
2125 // child is a paragraph
2126 wxRichTextObject* child = node->GetData();
2127 const wxRichTextRange& r = child->GetRange();
2128
2129 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2130 {
2131 startPara = node;
2132 break;
2133 }
2134
2135 node = node->GetNext();
2136 }
2137
2138 // Next find the last paragraph containing part of the range
2139 node = m_children.GetFirst();
2140 while (node)
2141 {
2142 // child is a paragraph
2143 wxRichTextObject* child = node->GetData();
2144 const wxRichTextRange& r = child->GetRange();
2145
2146 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2147 {
2148 endPara = node;
2149 break;
2150 }
2151
2152 node = node->GetNext();
2153 }
2154
2155 if (!startPara || !endPara)
2156 return false;
2157
2158 // Now we can add up the sizes
2159 for (node = startPara; node ; node = node->GetNext())
2160 {
2161 // child is a paragraph
2162 wxRichTextObject* child = node->GetData();
2163 const wxRichTextRange& childRange = child->GetRange();
2164 wxRichTextRange rangeToFind = range;
2165 rangeToFind.LimitTo(childRange);
2166
603f702b
JS
2167 if (child->IsTopLevel())
2168 rangeToFind = child->GetOwnRange();
2169
5d7836c4
JS
2170 wxSize childSize;
2171
2172 int childDescent = 0;
8db2e3ef 2173 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
5d7836c4
JS
2174
2175 descent = wxMax(childDescent, descent);
2176
2177 sz.x = wxMax(sz.x, childSize.x);
2178 sz.y += childSize.y;
2179
2180 if (node == endPara)
2181 break;
2182 }
2183
2184 size = sz;
2185
2186 return true;
2187}
2188
2189/// Get the paragraph at the given position
2190wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2191{
2192 if (caretPosition)
2193 pos ++;
2194
2195 // First find the first paragraph whose starting position is within the range.
2196 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2197 while (node)
2198 {
2199 // child is a paragraph
2200 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2201 // wxASSERT (child != NULL);
5d7836c4 2202
603f702b
JS
2203 if (child)
2204 {
2205 // Return first child in buffer if position is -1
2206 // if (pos == -1)
2207 // return child;
5d7836c4 2208
603f702b
JS
2209 if (child->GetRange().Contains(pos))
2210 return child;
2211 }
5d7836c4
JS
2212
2213 node = node->GetNext();
2214 }
2215 return NULL;
2216}
2217
2218/// Get the line at the given position
2219wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2220{
2221 if (caretPosition)
2222 pos ++;
2223
2224 // First find the first paragraph whose starting position is within the range.
2225 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2226 while (node)
2227 {
7051fa41
JS
2228 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2229 if (obj->GetRange().Contains(pos))
5d7836c4 2230 {
7051fa41
JS
2231 // child is a paragraph
2232 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
603f702b 2233 // wxASSERT (child != NULL);
7051fa41 2234
603f702b 2235 if (child)
7051fa41 2236 {
603f702b
JS
2237 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2238 while (node2)
2239 {
2240 wxRichTextLine* line = node2->GetData();
5d7836c4 2241
603f702b 2242 wxRichTextRange range = line->GetAbsoluteRange();
1e967276 2243
603f702b 2244 if (range.Contains(pos) ||
5d7836c4 2245
603f702b
JS
2246 // If the position is end-of-paragraph, then return the last line of
2247 // of the paragraph.
2248 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2249 return line;
5d7836c4 2250
603f702b
JS
2251 node2 = node2->GetNext();
2252 }
7051fa41 2253 }
7fe8059f 2254 }
5d7836c4
JS
2255
2256 node = node->GetNext();
2257 }
2258
2259 int lineCount = GetLineCount();
2260 if (lineCount > 0)
2261 return GetLineForVisibleLineNumber(lineCount-1);
2262 else
2263 return NULL;
2264}
2265
2266/// Get the line at the given y pixel position, or the last line.
2267wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2268{
2269 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2270 while (node)
2271 {
2272 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2273 // wxASSERT (child != NULL);
5d7836c4 2274
603f702b 2275 if (child)
5d7836c4 2276 {
603f702b
JS
2277 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2278 while (node2)
2279 {
2280 wxRichTextLine* line = node2->GetData();
5d7836c4 2281
603f702b 2282 wxRect rect(line->GetRect());
5d7836c4 2283
603f702b
JS
2284 if (y <= rect.GetBottom())
2285 return line;
5d7836c4 2286
603f702b
JS
2287 node2 = node2->GetNext();
2288 }
7fe8059f 2289 }
5d7836c4
JS
2290
2291 node = node->GetNext();
2292 }
2293
2294 // Return last line
2295 int lineCount = GetLineCount();
2296 if (lineCount > 0)
2297 return GetLineForVisibleLineNumber(lineCount-1);
2298 else
2299 return NULL;
2300}
2301
2302/// Get the number of visible lines
2303int wxRichTextParagraphLayoutBox::GetLineCount() const
2304{
2305 int count = 0;
2306
2307 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2308 while (node)
2309 {
2310 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b
JS
2311 // wxASSERT (child != NULL);
2312
2313 if (child)
2314 count += child->GetLines().GetCount();
5d7836c4 2315
5d7836c4
JS
2316 node = node->GetNext();
2317 }
2318 return count;
2319}
2320
2321
2322/// Get the paragraph for a given line
2323wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2324{
1e967276 2325 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
2326}
2327
2328/// Get the line size at the given position
2329wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2330{
2331 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2332 if (line)
2333 {
2334 return line->GetSize();
2335 }
2336 else
2337 return wxSize(0, 0);
2338}
2339
2340
2341/// Convenience function to add a paragraph of text
24777478 2342wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2343{
fe5aa22c 2344 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2345 // be combined at display time.
2346 // Divide into paragraph and character styles.
3e541562 2347
24777478
JS
2348 wxRichTextAttr defaultCharStyle;
2349 wxRichTextAttr defaultParaStyle;
4f32b3cf 2350
5607c890
JS
2351 // If the default style is a named paragraph style, don't apply any character formatting
2352 // to the initial text string.
2353 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2354 {
2355 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2356 if (def)
2357 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2358 }
2359 else
2360 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2361
24777478
JS
2362 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2363 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf
JS
2364
2365 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
32423dd8 2366 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4
JS
2367
2368 AppendChild(para);
2369
2370 UpdateRanges();
5d7836c4
JS
2371
2372 return para->GetRange();
2373}
2374
2375/// Adds multiple paragraphs, based on newlines.
24777478 2376wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
5d7836c4 2377{
fe5aa22c 2378 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2379 // be combined at display time.
2380 // Divide into paragraph and character styles.
3e541562 2381
24777478
JS
2382 wxRichTextAttr defaultCharStyle;
2383 wxRichTextAttr defaultParaStyle;
5607c890
JS
2384
2385 // If the default style is a named paragraph style, don't apply any character formatting
2386 // to the initial text string.
2387 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2388 {
2389 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2390 if (def)
2391 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2392 }
2393 else
2394 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2395
24777478
JS
2396 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2397 wxRichTextAttr* cStyle = & defaultCharStyle;
4f32b3cf 2398
5d7836c4
JS
2399 wxRichTextParagraph* firstPara = NULL;
2400 wxRichTextParagraph* lastPara = NULL;
2401
2402 wxRichTextRange range(-1, -1);
0ca07313 2403
5d7836c4 2404 size_t i = 0;
28f92d74 2405 size_t len = text.length();
5d7836c4 2406 wxString line;
4f32b3cf 2407 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2408 para->GetAttributes().GetTextBoxAttr().Reset();
0ca07313
JS
2409
2410 AppendChild(para);
2411
2412 firstPara = para;
2413 lastPara = para;
2414
5d7836c4
JS
2415 while (i < len)
2416 {
2417 wxChar ch = text[i];
2418 if (ch == wxT('\n') || ch == wxT('\r'))
2419 {
99404ab0
JS
2420 if (i != (len-1))
2421 {
2422 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2423 plainText->SetText(line);
0ca07313 2424
99404ab0 2425 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
32423dd8 2426 para->GetAttributes().GetTextBoxAttr().Reset();
5d7836c4 2427
99404ab0 2428 AppendChild(para);
0ca07313 2429
99404ab0
JS
2430 lastPara = para;
2431 line = wxEmptyString;
2432 }
5d7836c4
JS
2433 }
2434 else
2435 line += ch;
2436
2437 i ++;
2438 }
0ca07313 2439
7fe8059f 2440 if (!line.empty())
5d7836c4 2441 {
0ca07313
JS
2442 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2443 plainText->SetText(line);
5d7836c4
JS
2444 }
2445
5d7836c4 2446 UpdateRanges();
0ca07313 2447
0ca07313 2448 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
2449}
2450
2451/// Convenience function to add an image
24777478 2452wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
5d7836c4 2453{
fe5aa22c 2454 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
2455 // be combined at display time.
2456 // Divide into paragraph and character styles.
3e541562 2457
24777478
JS
2458 wxRichTextAttr defaultCharStyle;
2459 wxRichTextAttr defaultParaStyle;
5607c890
JS
2460
2461 // If the default style is a named paragraph style, don't apply any character formatting
2462 // to the initial text string.
2463 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2464 {
2465 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2466 if (def)
2467 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2468 }
2469 else
2470 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
5d7836c4 2471
24777478
JS
2472 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2473 wxRichTextAttr* cStyle = & defaultCharStyle;
5d7836c4 2474
4f32b3cf 2475 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
32423dd8 2476 para->GetAttributes().GetTextBoxAttr().Reset();
4f32b3cf
JS
2477 AppendChild(para);
2478 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 2479
5d7836c4 2480 UpdateRanges();
5d7836c4
JS
2481
2482 return para->GetRange();
2483}
2484
2485
2486/// Insert fragment into this box at the given position. If partialParagraph is true,
2487/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2488/// marker.
5d7836c4 2489
0ca07313 2490bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4 2491{
5d7836c4
JS
2492 // First, find the first paragraph whose starting position is within the range.
2493 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2494 if (para)
2495 {
24777478 2496 wxRichTextAttr originalAttr = para->GetAttributes();
99404ab0 2497
5d7836c4
JS
2498 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2499
2500 // Now split at this position, returning the object to insert the new
2501 // ones in front of.
2502 wxRichTextObject* nextObject = para->SplitAt(position);
2503
2504 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2505 // text, for example, so let's optimize.
2506
2507 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2508 {
2509 // Add the first para to this para...
2510 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2511 if (!firstParaNode)
2512 return false;
2513
2514 // Iterate through the fragment paragraph inserting the content into this paragraph.
2515 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2516 wxASSERT (firstPara != NULL);
2517
2518 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2519 while (objectNode)
2520 {
2521 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2522
5d7836c4
JS
2523 if (!nextObject)
2524 {
2525 // Append
2526 para->AppendChild(newObj);
2527 }
2528 else
2529 {
2530 // Insert before nextObject
2531 para->InsertChild(newObj, nextObject);
2532 }
7fe8059f 2533
5d7836c4
JS
2534 objectNode = objectNode->GetNext();
2535 }
2536
2537 return true;
2538 }
2539 else
2540 {
2541 // Procedure for inserting a fragment consisting of a number of
2542 // paragraphs:
2543 //
2544 // 1. Remove and save the content that's after the insertion point, for adding
2545 // back once we've added the fragment.
2546 // 2. Add the content from the first fragment paragraph to the current
2547 // paragraph.
2548 // 3. Add remaining fragment paragraphs after the current paragraph.
2549 // 4. Add back the saved content from the first paragraph. If partialParagraph
2550 // is true, add it to the last paragraph added and not a new one.
2551
2552 // 1. Remove and save objects after split point.
2553 wxList savedObjects;
2554 if (nextObject)
2555 para->MoveToList(nextObject, savedObjects);
2556
2557 // 2. Add the content from the 1st fragment paragraph.
2558 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2559 if (!firstParaNode)
2560 return false;
2561
2562 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2563 wxASSERT(firstPara != NULL);
2564
6c0ea513
JS
2565 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2566 para->SetAttributes(firstPara->GetAttributes());
99404ab0
JS
2567
2568 // Save empty paragraph attributes for appending later
2569 // These are character attributes deliberately set for a new paragraph. Without this,
2570 // we couldn't pass default attributes when appending a new paragraph.
24777478 2571 wxRichTextAttr emptyParagraphAttributes;
99404ab0 2572
5d7836c4 2573 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
99404ab0
JS
2574
2575 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2576 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2577
5d7836c4
JS
2578 while (objectNode)
2579 {
c025e094 2580 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 2581
c025e094
JS
2582 // Append
2583 para->AppendChild(newObj);
7fe8059f 2584
5d7836c4
JS
2585 objectNode = objectNode->GetNext();
2586 }
2587
2588 // 3. Add remaining fragment paragraphs after the current paragraph.
2589 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2590 wxRichTextObject* nextParagraph = NULL;
2591 if (nextParagraphNode)
2592 nextParagraph = nextParagraphNode->GetData();
2593
2594 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2595 wxRichTextParagraph* finalPara = para;
2596
99404ab0
JS
2597 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2598
5d7836c4 2599 // If there was only one paragraph, we need to insert a new one.
99404ab0 2600 while (i)
5d7836c4 2601 {
99404ab0
JS
2602 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2603 wxASSERT( para != NULL );
5d7836c4 2604
99404ab0 2605 finalPara = (wxRichTextParagraph*) para->Clone();
5d7836c4
JS
2606
2607 if (nextParagraph)
2608 InsertChild(finalPara, nextParagraph);
2609 else
7fe8059f 2610 AppendChild(finalPara);
99404ab0
JS
2611
2612 i = i->GetNext();
5d7836c4 2613 }
5d7836c4 2614
99404ab0
JS
2615 // If there was only one paragraph, or we have full paragraphs in our fragment,
2616 // we need to insert a new one.
2617 if (needExtraPara)
2618 {
2619 finalPara = new wxRichTextParagraph;
5d7836c4
JS
2620
2621 if (nextParagraph)
2622 InsertChild(finalPara, nextParagraph);
2623 else
2624 AppendChild(finalPara);
5d7836c4
JS
2625 }
2626
2627 // 4. Add back the remaining content.
2628 if (finalPara)
2629 {
c025e094
JS
2630 if (nextObject)
2631 finalPara->MoveFromList(savedObjects);
5d7836c4
JS
2632
2633 // Ensure there's at least one object
2634 if (finalPara->GetChildCount() == 0)
2635 {
7fe8059f 2636 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
99404ab0 2637 text->SetAttributes(emptyParagraphAttributes);
5d7836c4
JS
2638
2639 finalPara->AppendChild(text);
2640 }
2641 }
2642
6c0ea513
JS
2643 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2644 finalPara->SetAttributes(firstPara->GetAttributes());
2645 else if (finalPara && finalPara != para)
99404ab0
JS
2646 finalPara->SetAttributes(originalAttr);
2647
5d7836c4
JS
2648 return true;
2649 }
2650 }
2651 else
2652 {
2653 // Append
2654 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2655 while (i)
2656 {
2657 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2658 wxASSERT( para != NULL );
7fe8059f 2659
5d7836c4 2660 AppendChild(para->Clone());
7fe8059f 2661
5d7836c4
JS
2662 i = i->GetNext();
2663 }
2664
2665 return true;
2666 }
5d7836c4
JS
2667}
2668
2669/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2670/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 2671bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
2672{
2673 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2674 while (i)
2675 {
2676 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2677 wxASSERT( para != NULL );
2678
2679 if (!para->GetRange().IsOutside(range))
2680 {
2681 fragment.AppendChild(para->Clone());
7fe8059f 2682 }
5d7836c4
JS
2683 i = i->GetNext();
2684 }
2685
2686 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2687 if (!fragment.IsEmpty())
2688 {
5d7836c4
JS
2689 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2690 wxASSERT( firstPara != NULL );
2691
0e190fa2
JS
2692 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2693 wxASSERT( lastPara != NULL );
2694
2695 if (!firstPara || !lastPara)
2696 return false;
2697
2698 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2699
2700 long firstPos = firstPara->GetRange().GetStart();
2701
2702 // Adjust for renumbering from zero
2703 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2704
2705 long end;
2706 fragment.CalculateRange(0, end);
2707
5d7836c4 2708 // Chop off the start of the paragraph
0e190fa2 2709 if (topTailRange.GetStart() > 0)
5d7836c4 2710 {
0e190fa2 2711 wxRichTextRange r(0, topTailRange.GetStart()-1);
5d7836c4
JS
2712 firstPara->DeleteRange(r);
2713
2714 // Make sure the numbering is correct
0e190fa2 2715 fragment.CalculateRange(0, end);
5d7836c4
JS
2716
2717 // Now, we've deleted some positions, so adjust the range
2718 // accordingly.
0e190fa2
JS
2719 topTailRange.SetStart(range.GetLength());
2720 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2721 }
2722 else
2723 {
2724 topTailRange.SetStart(range.GetLength());
2725 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
5d7836c4
JS
2726 }
2727
61e6149e 2728 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
5d7836c4 2729 {
0e190fa2 2730 lastPara->DeleteRange(topTailRange);
5d7836c4
JS
2731
2732 // Make sure the numbering is correct
2733 long end;
0e190fa2 2734 fragment.CalculateRange(0, end);
5d7836c4
JS
2735
2736 // We only have part of a paragraph at the end
2737 fragment.SetPartialParagraph(true);
2738 }
2739 else
2740 {
0e190fa2
JS
2741 // We have a partial paragraph (don't save last new paragraph marker)
2742 // or complete paragraph
2743 fragment.SetPartialParagraph(isFragment);
5d7836c4
JS
2744 }
2745 }
2746
2747 return true;
2748}
2749
2750/// Given a position, get the number of the visible line (potentially many to a paragraph),
2751/// starting from zero at the start of the buffer.
2752long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2753{
2754 if (caretPosition)
2755 pos ++;
2756
2757 int lineCount = 0;
2758
2759 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2760 while (node)
2761 {
2762 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2763 // wxASSERT( child != NULL );
5d7836c4 2764
603f702b 2765 if (child)
5d7836c4 2766 {
603f702b 2767 if (child->GetRange().Contains(pos))
5d7836c4 2768 {
603f702b
JS
2769 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2770 while (node2)
5d7836c4 2771 {
603f702b
JS
2772 wxRichTextLine* line = node2->GetData();
2773 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 2774
603f702b
JS
2775 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2776 {
2777 // If the caret is displayed at the end of the previous wrapped line,
2778 // we want to return the line it's _displayed_ at (not the actual line
2779 // containing the position).
2780 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2781 return lineCount - 1;
2782 else
2783 return lineCount;
2784 }
7fe8059f 2785
603f702b
JS
2786 lineCount ++;
2787
2788 node2 = node2->GetNext();
2789 }
2790 // If we didn't find it in the lines, it must be
2791 // the last position of the paragraph. So return the last line.
2792 return lineCount-1;
5d7836c4 2793 }
603f702b
JS
2794 else
2795 lineCount += child->GetLines().GetCount();
5d7836c4 2796 }
5d7836c4
JS
2797
2798 node = node->GetNext();
2799 }
2800
2801 // Not found
2802 return -1;
2803}
2804
2805/// Given a line number, get the corresponding wxRichTextLine object.
2806wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2807{
2808 int lineCount = 0;
2809
2810 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2811 while (node)
2812 {
2813 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2814 // wxASSERT(child != NULL);
5d7836c4 2815
603f702b 2816 if (child)
5d7836c4 2817 {
603f702b 2818 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
5d7836c4 2819 {
603f702b
JS
2820 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2821 while (node2)
2822 {
2823 wxRichTextLine* line = node2->GetData();
7fe8059f 2824
603f702b
JS
2825 if (lineCount == lineNumber)
2826 return line;
5d7836c4 2827
603f702b 2828 lineCount ++;
7fe8059f 2829
603f702b
JS
2830 node2 = node2->GetNext();
2831 }
7fe8059f 2832 }
603f702b
JS
2833 else
2834 lineCount += child->GetLines().GetCount();
5d7836c4 2835 }
5d7836c4
JS
2836
2837 node = node->GetNext();
2838 }
2839
2840 // Didn't find it
2841 return NULL;
2842}
2843
2844/// Delete range from layout.
2845bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2846{
2847 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 2848
99404ab0 2849 wxRichTextParagraph* firstPara = NULL;
5d7836c4
JS
2850 while (node)
2851 {
2852 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 2853 // wxASSERT (obj != NULL);
5d7836c4
JS
2854
2855 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 2856
603f702b 2857 if (obj)
5d7836c4 2858 {
603f702b 2859 // Delete the range in each paragraph
99404ab0 2860
603f702b 2861 if (!obj->GetRange().IsOutside(range))
5d7836c4 2862 {
603f702b
JS
2863 // Deletes the content of this object within the given range
2864 obj->DeleteRange(range);
99404ab0 2865
603f702b
JS
2866 wxRichTextRange thisRange = obj->GetRange();
2867 wxRichTextAttr thisAttr = obj->GetAttributes();
5d7836c4 2868
603f702b
JS
2869 // If the whole paragraph is within the range to delete,
2870 // delete the whole thing.
2871 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
5d7836c4 2872 {
603f702b
JS
2873 // Delete the whole object
2874 RemoveChild(obj, true);
2875 obj = NULL;
99404ab0 2876 }
603f702b
JS
2877 else if (!firstPara)
2878 firstPara = obj;
5d7836c4 2879
603f702b
JS
2880 // If the range includes the paragraph end, we need to join this
2881 // and the next paragraph.
2882 if (range.GetEnd() <= thisRange.GetEnd())
6c0ea513 2883 {
603f702b
JS
2884 // We need to move the objects from the next paragraph
2885 // to this paragraph
2886
2887 wxRichTextParagraph* nextParagraph = NULL;
2888 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2889 nextParagraph = obj;
6c0ea513 2890 else
603f702b
JS
2891 {
2892 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2893 if (next)
2894 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2895 }
5d7836c4 2896
603f702b
JS
2897 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2898
2899 wxRichTextAttr nextParaAttr;
2900 if (applyFinalParagraphStyle)
2901 {
2902 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2903 // not the next one.
2904 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2905 nextParaAttr = thisAttr;
2906 else
2907 nextParaAttr = nextParagraph->GetAttributes();
2908 }
5d7836c4 2909
603f702b 2910 if (firstPara && nextParagraph && firstPara != nextParagraph)
99404ab0 2911 {
603f702b
JS
2912 // Move the objects to the previous para
2913 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
5d7836c4 2914
603f702b
JS
2915 while (node1)
2916 {
2917 wxRichTextObject* obj1 = node1->GetData();
5d7836c4 2918
603f702b 2919 firstPara->AppendChild(obj1);
5d7836c4 2920
603f702b
JS
2921 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2922 nextParagraph->GetChildren().Erase(node1);
99404ab0 2923
603f702b
JS
2924 node1 = next1;
2925 }
5d7836c4 2926
603f702b
JS
2927 // Delete the paragraph
2928 RemoveChild(nextParagraph, true);
2929 }
fa01bfdd 2930
603f702b
JS
2931 // Avoid empty paragraphs
2932 if (firstPara && firstPara->GetChildren().GetCount() == 0)
2933 {
2934 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2935 firstPara->AppendChild(text);
2936 }
99404ab0 2937
603f702b
JS
2938 if (applyFinalParagraphStyle)
2939 firstPara->SetAttributes(nextParaAttr);
2940
2941 return true;
2942 }
5d7836c4
JS
2943 }
2944 }
7fe8059f 2945
5d7836c4
JS
2946 node = next;
2947 }
7fe8059f 2948
5d7836c4
JS
2949 return true;
2950}
2951
2952/// Get any text in this object for the given range
2953wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
2954{
2955 int lineCount = 0;
2956 wxString text;
2957 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2958 while (node)
2959 {
2960 wxRichTextObject* child = node->GetData();
2961 if (!child->GetRange().IsOutside(range))
2962 {
5d7836c4
JS
2963 wxRichTextRange childRange = range;
2964 childRange.LimitTo(child->GetRange());
7fe8059f 2965
5d7836c4 2966 wxString childText = child->GetTextForRange(childRange);
7fe8059f 2967
5d7836c4
JS
2968 text += childText;
2969
1a75935d 2970 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
fe5aa22c
JS
2971 text += wxT("\n");
2972
5d7836c4
JS
2973 lineCount ++;
2974 }
2975 node = node->GetNext();
2976 }
2977
2978 return text;
2979}
2980
2981/// Get all the text
2982wxString wxRichTextParagraphLayoutBox::GetText() const
2983{
c99f1b0f 2984 return GetTextForRange(GetOwnRange());
5d7836c4
JS
2985}
2986
2987/// Get the paragraph by number
2988wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
2989{
27e20452 2990 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
2991 return NULL;
2992
2993 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
2994}
2995
2996/// Get the length of the paragraph
2997int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
2998{
2999 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3000 if (para)
3001 return para->GetRange().GetLength() - 1; // don't include newline
3002 else
3003 return 0;
3004}
3005
3006/// Get the text of the paragraph
3007wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3008{
3009 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3010 if (para)
3011 return para->GetTextForRange(para->GetRange());
3012 else
3013 return wxEmptyString;
3014}
3015
3016/// Convert zero-based line column and paragraph number to a position.
3017long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3018{
3019 wxRichTextParagraph* para = GetParagraphAtLine(y);
3020 if (para)
3021 {
3022 return para->GetRange().GetStart() + x;
3023 }
3024 else
3025 return -1;
3026}
3027
3028/// Convert zero-based position to line column and paragraph number
3029bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3030{
3031 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3032 if (para)
3033 {
3034 int count = 0;
3035 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3036 while (node)
3037 {
3038 wxRichTextObject* child = node->GetData();
3039 if (child == para)
3040 break;
3041 count ++;
3042 node = node->GetNext();
3043 }
3044
3045 *y = count;
3046 *x = pos - para->GetRange().GetStart();
3047
3048 return true;
3049 }
3050 else
3051 return false;
3052}
3053
3054/// Get the leaf object in a paragraph at this position.
3055/// Given a line number, get the corresponding wxRichTextLine object.
3056wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3057{
3058 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3059 if (para)
3060 {
3061 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 3062
5d7836c4
JS
3063 while (node)
3064 {
3065 wxRichTextObject* child = node->GetData();
3066 if (child->GetRange().Contains(position))
3067 return child;
7fe8059f 3068
5d7836c4
JS
3069 node = node->GetNext();
3070 }
3071 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3072 return para->GetChildren().GetLast()->GetData();
3073 }
3074 return NULL;
3075}
3076
3077/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
24777478 3078bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
3079{
3080 bool characterStyle = false;
3081 bool paragraphStyle = false;
3082
3083 if (style.IsCharacterStyle())
3084 characterStyle = true;
3085 if (style.IsParagraphStyle())
3086 paragraphStyle = true;
3087
603f702b
JS
3088 wxRichTextBuffer* buffer = GetBuffer();
3089
59509217
JS
3090 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3091 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3092 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3093 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14 3094 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
aeb6ebe2 3095 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
523d2f14
JS
3096
3097 // Apply paragraph style first, if any
24777478 3098 wxRichTextAttr wholeStyle(style);
523d2f14 3099
603f702b 3100 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
523d2f14 3101 {
603f702b 3102 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
523d2f14 3103 if (def)
603f702b 3104 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14 3105 }
59509217
JS
3106
3107 // Limit the attributes to be set to the content to only character attributes.
24777478 3108 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
3109 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3110
603f702b 3111 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
523d2f14 3112 {
603f702b 3113 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
523d2f14 3114 if (def)
603f702b 3115 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
523d2f14
JS
3116 }
3117
5d7836c4
JS
3118 // If we are associated with a control, make undoable; otherwise, apply immediately
3119 // to the data.
3120
603f702b 3121 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
5d7836c4
JS
3122
3123 wxRichTextAction* action = NULL;
7fe8059f 3124
5d7836c4
JS
3125 if (haveControl && withUndo)
3126 {
603f702b 3127 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
5d7836c4 3128 action->SetRange(range);
603f702b 3129 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
5d7836c4
JS
3130 }
3131
3132 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3133 while (node)
3134 {
3135 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3136 // wxASSERT (para != NULL);
5d7836c4
JS
3137
3138 if (para && para->GetChildCount() > 0)
3139 {
3140 // Stop searching if we're beyond the range of interest
3141 if (para->GetRange().GetStart() > range.GetEnd())
3142 break;
3143
3144 if (!para->GetRange().IsOutside(range))
3145 {
3146 // We'll be using a copy of the paragraph to make style changes,
3147 // not updating the buffer directly.
4e09ebe8 3148 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 3149
5d7836c4
JS
3150 if (haveControl && withUndo)
3151 {
3152 newPara = new wxRichTextParagraph(*para);
3153 action->GetNewParagraphs().AppendChild(newPara);
3154
3155 // Also store the old ones for Undo
3156 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3157 }
3158 else
3159 newPara = para;
41a85215 3160
a7ed48a5
JS
3161 // If we're specifying paragraphs only, then we really mean character formatting
3162 // to be included in the paragraph style
3163 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 3164 {
aeb6ebe2
JS
3165 if (removeStyle)
3166 {
3167 // Removes the given style from the paragraph
3168 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3169 }
3170 else if (resetExistingStyle)
523d2f14
JS
3171 newPara->GetAttributes() = wholeStyle;
3172 else
59509217 3173 {
523d2f14
JS
3174 if (applyMinimal)
3175 {
3176 // Only apply attributes that will make a difference to the combined
3177 // style as seen on the display
603f702b 3178 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
523d2f14
JS
3179 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3180 }
3181 else
3182 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 3183 }
59509217 3184 }
5d7836c4 3185
5912d19e 3186 // When applying paragraph styles dynamically, don't change the text objects' attributes
fe5aa22c
JS
3187 // since they will computed as needed. Only apply the character styling if it's _only_
3188 // character styling. This policy is subject to change and might be put under user control.
3189
59509217
JS
3190 // Hm. we might well be applying a mix of paragraph and character styles, in which
3191 // case we _do_ want to apply character styles regardless of what para styles are set.
3192 // But if we're applying a paragraph style, which has some character attributes, but
3193 // we only want the paragraphs to hold this character style, then we _don't_ want to
3194 // apply the character style. So we need to be able to choose.
3195
f1d800d9 3196 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
5d7836c4
JS
3197 {
3198 wxRichTextRange childRange(range);
3199 childRange.LimitTo(newPara->GetRange());
7fe8059f 3200
5d7836c4
JS
3201 // Find the starting position and if necessary split it so
3202 // we can start applying a different style.
3203 // TODO: check that the style actually changes or is different
3204 // from style outside of range
4e09ebe8
JS
3205 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3206 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 3207
5d7836c4
JS
3208 if (childRange.GetStart() == newPara->GetRange().GetStart())
3209 firstObject = newPara->GetChildren().GetFirst()->GetData();
3210 else
3211 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 3212
5d7836c4
JS
3213 // Increment by 1 because we're apply the style one _after_ the split point
3214 long splitPoint = childRange.GetEnd();
3215 if (splitPoint != newPara->GetRange().GetEnd())
3216 splitPoint ++;
7fe8059f 3217
5d7836c4 3218 // Find last object
4b3483e7 3219 if (splitPoint == newPara->GetRange().GetEnd())
5d7836c4
JS
3220 lastObject = newPara->GetChildren().GetLast()->GetData();
3221 else
3222 // lastObject is set as a side-effect of splitting. It's
3223 // returned as the object before the new object.
3224 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 3225
5d7836c4
JS
3226 wxASSERT(firstObject != NULL);
3227 wxASSERT(lastObject != NULL);
7fe8059f 3228
5d7836c4
JS
3229 if (!firstObject || !lastObject)
3230 continue;
7fe8059f 3231
5d7836c4
JS
3232 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3233 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 3234
4c9847e1
MW
3235 wxASSERT(firstNode);
3236 wxASSERT(lastNode);
7fe8059f 3237
5d7836c4 3238 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 3239
5d7836c4
JS
3240 while (node2)
3241 {
3242 wxRichTextObject* child = node2->GetData();
7fe8059f 3243
aeb6ebe2
JS
3244 if (removeStyle)
3245 {
3246 // Removes the given style from the paragraph
3247 wxRichTextRemoveStyle(child->GetAttributes(), style);
3248 }
3249 else if (resetExistingStyle)
523d2f14
JS
3250 child->GetAttributes() = characterAttributes;
3251 else
59509217 3252 {
523d2f14
JS
3253 if (applyMinimal)
3254 {
3255 // Only apply attributes that will make a difference to the combined
3256 // style as seen on the display
603f702b 3257 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
523d2f14
JS
3258 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3259 }
3260 else
3261 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 3262 }
59509217 3263
5d7836c4
JS
3264 if (node2 == lastNode)
3265 break;
7fe8059f 3266
5d7836c4
JS
3267 node2 = node2->GetNext();
3268 }
3269 }
3270 }
3271 }
3272
3273 node = node->GetNext();
3274 }
3275
3276 // Do action, or delay it until end of batch.
3277 if (haveControl && withUndo)
603f702b 3278 buffer->SubmitAction(action);
5d7836c4
JS
3279
3280 return true;
3281}
3282
603f702b
JS
3283// Just change the attributes for this single object.
3284void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
cdaed652 3285{
603f702b 3286 wxRichTextBuffer* buffer = GetBuffer();
cdaed652 3287 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
603f702b
JS
3288 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3289 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3290
cdaed652 3291 wxRichTextAction *action = NULL;
603f702b
JS
3292 wxRichTextAttr newAttr = obj->GetAttributes();
3293 if (resetExistingStyle)
3294 newAttr = textAttr;
3295 else
3296 newAttr.Apply(textAttr);
cdaed652
VZ
3297
3298 if (haveControl && withUndo)
3299 {
603f702b
JS
3300 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3301 action->SetRange(obj->GetRange().FromInternal());
3302 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3303 action->MakeObject(obj);
bec80f4f 3304
603f702b 3305 action->GetAttributes() = newAttr;
cdaed652
VZ
3306 }
3307 else
603f702b 3308 obj->GetAttributes() = newAttr;
cdaed652
VZ
3309
3310 if (haveControl && withUndo)
603f702b 3311 buffer->SubmitAction(action);
cdaed652
VZ
3312}
3313
5d7836c4 3314/// Get the text attributes for this position.
24777478 3315bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
5d7836c4 3316{
fe5aa22c
JS
3317 return DoGetStyle(position, style, true);
3318}
e191ee87 3319
24777478 3320bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
fe5aa22c
JS
3321{
3322 return DoGetStyle(position, style, false);
3323}
3324
fe5aa22c
JS
3325/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3326/// context attributes.
24777478 3327bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
5d7836c4 3328{
4e09ebe8 3329 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 3330
5d7836c4 3331 if (style.IsParagraphStyle())
fe5aa22c 3332 {
5d7836c4 3333 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
3334 if (obj)
3335 {
fe5aa22c
JS
3336 if (combineStyles)
3337 {
3338 // Start with the base style
3339 style = GetAttributes();
32423dd8 3340 style.GetTextBoxAttr().Reset();
e191ee87 3341
fe5aa22c
JS
3342 // Apply the paragraph style
3343 wxRichTextApplyStyle(style, obj->GetAttributes());
3344 }
3345 else
3346 style = obj->GetAttributes();
5912d19e 3347
fe5aa22c
JS
3348 return true;
3349 }
5d7836c4
JS
3350 }
3351 else
fe5aa22c
JS
3352 {
3353 obj = GetLeafObjectAtPosition(position);
3354 if (obj)
3355 {
fe5aa22c
JS
3356 if (combineStyles)
3357 {
3358 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3359 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3360 }
3361 else
3362 style = obj->GetAttributes();
5912d19e 3363
fe5aa22c
JS
3364 return true;
3365 }
fe5aa22c
JS
3366 }
3367 return false;
5d7836c4
JS
3368}
3369
59509217
JS
3370static bool wxHasStyle(long flags, long style)
3371{
3372 return (flags & style) != 0;
3373}
3374
3375/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3376/// content.
24777478
JS
3377bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3378{
3379 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3380
3381 return true;
3382}
3383
3384/// Get the combined style for a range - if any attribute is different within the range,
3385/// that attribute is not present within the flags.
3386/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3387/// nested.
3388bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
59509217 3389{
24777478
JS
3390 style = wxRichTextAttr();
3391
3392 wxRichTextAttr clashingAttr;
3393 wxRichTextAttr absentAttrPara, absentAttrChar;
d1e5be0e 3394
24777478
JS
3395 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3396 while (node)
59509217 3397 {
603f702b
JS
3398 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3399 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
59509217 3400 {
24777478 3401 if (para->GetChildren().GetCount() == 0)
59509217 3402 {
603f702b 3403 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
59509217 3404
24777478 3405 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
59509217
JS
3406 }
3407 else
3408 {
24777478
JS
3409 wxRichTextRange paraRange(para->GetRange());
3410 paraRange.LimitTo(range);
59509217 3411
24777478
JS
3412 // First collect paragraph attributes only
3413 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3414 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3415 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
9c4cb611 3416
24777478
JS
3417 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3418
3419 while (childNode)
59509217 3420 {
24777478
JS
3421 wxRichTextObject* child = childNode->GetData();
3422 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3423 {
603f702b 3424 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
59509217 3425
24777478
JS
3426 // Now collect character attributes only
3427 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
59509217 3428
24777478
JS
3429 CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
3430 }
59509217 3431
24777478 3432 childNode = childNode->GetNext();
59509217
JS
3433 }
3434 }
59509217 3435 }
24777478 3436 node = node->GetNext();
59509217 3437 }
24777478
JS
3438 return true;
3439}
59509217 3440
24777478
JS
3441/// Set default style
3442bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3443{
3444 m_defaultAttributes = style;
3445 return true;
3446}
59509217 3447
24777478
JS
3448/// Test if this whole range has character attributes of the specified kind. If any
3449/// of the attributes are different within the range, the test fails. You
3450/// can use this to implement, for example, bold button updating. style must have
3451/// flags indicating which attributes are of interest.
3452bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3453{
3454 int foundCount = 0;
3455 int matchingCount = 0;
59509217 3456
24777478
JS
3457 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3458 while (node)
59509217 3459 {
24777478 3460 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3461 // wxASSERT (para != NULL);
59509217 3462
24777478 3463 if (para)
59509217 3464 {
24777478
JS
3465 // Stop searching if we're beyond the range of interest
3466 if (para->GetRange().GetStart() > range.GetEnd())
3467 return foundCount == matchingCount && foundCount != 0;
59509217 3468
24777478 3469 if (!para->GetRange().IsOutside(range))
59509217 3470 {
24777478 3471 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
59509217 3472
24777478
JS
3473 while (node2)
3474 {
3475 wxRichTextObject* child = node2->GetData();
3476 // Allow for empty string if no buffer
3477 wxRichTextRange childRange = child->GetRange();
3478 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3479 childRange.SetEnd(childRange.GetEnd()+1);
59509217 3480
345c78ca 3481 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
24777478
JS
3482 {
3483 foundCount ++;
3484 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
59509217 3485
32423dd8 3486 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
24777478
JS
3487 matchingCount ++;
3488 }
59509217 3489
24777478
JS
3490 node2 = node2->GetNext();
3491 }
59509217
JS
3492 }
3493 }
59509217 3494
24777478 3495 node = node->GetNext();
59509217
JS
3496 }
3497
24777478
JS
3498 return foundCount == matchingCount && foundCount != 0;
3499}
59509217 3500
24777478
JS
3501/// Test if this whole range has paragraph attributes of the specified kind. If any
3502/// of the attributes are different within the range, the test fails. You
3503/// can use this to implement, for example, centering button updating. style must have
3504/// flags indicating which attributes are of interest.
3505bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3506{
3507 int foundCount = 0;
3508 int matchingCount = 0;
3509
3510 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3511 while (node)
38f833b1 3512 {
24777478 3513 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3514 // wxASSERT (para != NULL);
24777478
JS
3515
3516 if (para)
38f833b1 3517 {
24777478
JS
3518 // Stop searching if we're beyond the range of interest
3519 if (para->GetRange().GetStart() > range.GetEnd())
3520 return foundCount == matchingCount && foundCount != 0;
3521
3522 if (!para->GetRange().IsOutside(range))
38f833b1 3523 {
24777478
JS
3524 wxRichTextAttr textAttr = GetAttributes();
3525 // Apply the paragraph style
3526 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3527
3528 foundCount ++;
32423dd8 3529 if (textAttr.EqPartial(style, false /* strong test */))
24777478 3530 matchingCount ++;
38f833b1
JS
3531 }
3532 }
24777478
JS
3533
3534 node = node->GetNext();
38f833b1 3535 }
24777478
JS
3536 return foundCount == matchingCount && foundCount != 0;
3537}
5d7836c4 3538
cc2aecde
JS
3539void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3540{
3541 wxRichTextBuffer* buffer = GetBuffer();
3542 if (buffer && buffer->GetRichTextCtrl())
3543 buffer->GetRichTextCtrl()->PrepareContent(container);
3544}
3545
590a0f8b
JS
3546/// Set character or paragraph properties
3547bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3548{
3549 wxRichTextBuffer* buffer = GetBuffer();
3550
3551 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3552 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3553 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3554 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3555 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3556
3557 // If we are associated with a control, make undoable; otherwise, apply immediately
3558 // to the data.
3559
3560 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3561
3562 wxRichTextAction* action = NULL;
3563
3564 if (haveControl && withUndo)
3565 {
3566 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3567 action->SetRange(range);
3568 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3569 }
3570
3571 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3572 while (node)
3573 {
3574 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3575 // wxASSERT (para != NULL);
3576
3577 if (para && para->GetChildCount() > 0)
3578 {
3579 // Stop searching if we're beyond the range of interest
3580 if (para->GetRange().GetStart() > range.GetEnd())
3581 break;
3582
3583 if (!para->GetRange().IsOutside(range))
3584 {
3585 // We'll be using a copy of the paragraph to make style changes,
3586 // not updating the buffer directly.
3587 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3588
3589 if (haveControl && withUndo)
3590 {
3591 newPara = new wxRichTextParagraph(*para);
3592 action->GetNewParagraphs().AppendChild(newPara);
3593
3594 // Also store the old ones for Undo
3595 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3596 }
3597 else
3598 newPara = para;
3599
3600 if (parasOnly)
3601 {
3602 if (removeProperties)
3603 {
3604 // Removes the given style from the paragraph
3605 // TODO
3606 newPara->GetProperties().RemoveProperties(properties);
3607 }
3608 else if (resetExistingProperties)
3609 newPara->GetProperties() = properties;
3610 else
3611 newPara->GetProperties().MergeProperties(properties);
3612 }
3613
3614 // When applying paragraph styles dynamically, don't change the text objects' attributes
3615 // since they will computed as needed. Only apply the character styling if it's _only_
3616 // character styling. This policy is subject to change and might be put under user control.
3617
3618 // Hm. we might well be applying a mix of paragraph and character styles, in which
3619 // case we _do_ want to apply character styles regardless of what para styles are set.
3620 // But if we're applying a paragraph style, which has some character attributes, but
3621 // we only want the paragraphs to hold this character style, then we _don't_ want to
3622 // apply the character style. So we need to be able to choose.
3623
3624 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3625 {
3626 wxRichTextRange childRange(range);
3627 childRange.LimitTo(newPara->GetRange());
3628
3629 // Find the starting position and if necessary split it so
3630 // we can start applying different properties.
3631 // TODO: check that the properties actually change or are different
3632 // from properties outside of range
3633 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3634 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3635
3636 if (childRange.GetStart() == newPara->GetRange().GetStart())
3637 firstObject = newPara->GetChildren().GetFirst()->GetData();
3638 else
3639 firstObject = newPara->SplitAt(range.GetStart());
3640
3641 // Increment by 1 because we're apply the style one _after_ the split point
3642 long splitPoint = childRange.GetEnd();
3643 if (splitPoint != newPara->GetRange().GetEnd())
3644 splitPoint ++;
3645
3646 // Find last object
3647 if (splitPoint == newPara->GetRange().GetEnd())
3648 lastObject = newPara->GetChildren().GetLast()->GetData();
3649 else
3650 // lastObject is set as a side-effect of splitting. It's
3651 // returned as the object before the new object.
3652 (void) newPara->SplitAt(splitPoint, & lastObject);
3653
3654 wxASSERT(firstObject != NULL);
3655 wxASSERT(lastObject != NULL);
3656
3657 if (!firstObject || !lastObject)
3658 continue;
3659
3660 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3661 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3662
3663 wxASSERT(firstNode);
3664 wxASSERT(lastNode);
3665
3666 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3667
3668 while (node2)
3669 {
3670 wxRichTextObject* child = node2->GetData();
3671
3672 if (removeProperties)
3673 {
3674 // Removes the given properties from the paragraph
3675 child->GetProperties().RemoveProperties(properties);
3676 }
3677 else if (resetExistingProperties)
3678 child->GetProperties() = properties;
3679 else
3680 {
3681 child->GetProperties().MergeProperties(properties);
3682 }
3683
3684 if (node2 == lastNode)
3685 break;
3686
3687 node2 = node2->GetNext();
3688 }
3689 }
3690 }
3691 }
3692
3693 node = node->GetNext();
3694 }
3695
3696 // Do action, or delay it until end of batch.
3697 if (haveControl && withUndo)
3698 buffer->SubmitAction(action);
3699
3700 return true;
3701}
3702
5d7836c4
JS
3703void wxRichTextParagraphLayoutBox::Reset()
3704{
3705 Clear();
3706
603f702b
JS
3707 wxRichTextBuffer* buffer = GetBuffer();
3708 if (buffer && buffer->GetRichTextCtrl())
cd8ba0d9 3709 {
603f702b
JS
3710 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3711 event.SetEventObject(buffer->GetRichTextCtrl());
3712 event.SetContainer(this);
cd8ba0d9
JS
3713
3714 buffer->SendEvent(event, true);
3715 }
3716
7fe8059f 3717 AddParagraph(wxEmptyString);
3e541562 3718
cc2aecde
JS
3719 PrepareContent(*this);
3720
603f702b 3721 InvalidateHierarchy(wxRICHTEXT_ALL);
5d7836c4
JS
3722}
3723
38113684
JS
3724/// Invalidate the buffer. With no argument, invalidates whole buffer.
3725void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3726{
603f702b 3727 wxRichTextCompositeObject::Invalidate(invalidRange);
39a1c2f2 3728
603f702b
JS
3729 DoInvalidate(invalidRange);
3730}
3731
3732// Do the (in)validation for this object only
3733void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3734{
1e967276 3735 if (invalidRange == wxRICHTEXT_ALL)
38113684 3736 {
1e967276 3737 m_invalidRange = wxRICHTEXT_ALL;
38113684 3738 }
1e967276 3739 // Already invalidating everything
603f702b
JS
3740 else if (m_invalidRange == wxRICHTEXT_ALL)
3741 {
3742 }
3743 else
3744 {
3745 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3746 m_invalidRange.SetStart(invalidRange.GetStart());
3747 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3748 m_invalidRange.SetEnd(invalidRange.GetEnd());
3749 }
3750}
39a1c2f2 3751
603f702b
JS
3752// Do the (in)validation both up and down the hierarchy
3753void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3754{
3755 Invalidate(invalidRange);
3756
3757 if (invalidRange != wxRICHTEXT_NONE)
3758 {
3759 // Now go up the hierarchy
3760 wxRichTextObject* thisObj = this;
3761 wxRichTextObject* p = GetParent();
3762 while (p)
3763 {
3764 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3765 if (l)
3766 l->DoInvalidate(thisObj->GetRange());
3767
3768 thisObj = p;
3769 p = p->GetParent();
3770 }
3771 }
38113684
JS
3772}
3773
3774/// Get invalid range, rounding to entire paragraphs if argument is true.
3775wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3776{
1e967276 3777 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 3778 return m_invalidRange;
39a1c2f2 3779
38113684 3780 wxRichTextRange range = m_invalidRange;
39a1c2f2 3781
38113684
JS
3782 if (wholeParagraphs)
3783 {
3784 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
38113684
JS
3785 if (para1)
3786 range.SetStart(para1->GetRange().GetStart());
cdaed652 3787 // floating layout make all child should be relayout
603f702b 3788 range.SetEnd(GetOwnRange().GetEnd());
38113684
JS
3789 }
3790 return range;
3791}
3792
fe5aa22c
JS
3793/// Apply the style sheet to the buffer, for example if the styles have changed.
3794bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3795{
3796 wxASSERT(styleSheet != NULL);
3797 if (!styleSheet)
3798 return false;
3799
3800 int foundCount = 0;
3801
44580804
JS
3802 wxRichTextAttr attr(GetBasicStyle());
3803 if (GetBasicStyle().HasParagraphStyleName())
3804 {
3805 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3806 if (paraDef)
3807 {
3808 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3809 SetBasicStyle(attr);
3810 foundCount ++;
3811 }
3812 }
3813
3814 if (GetBasicStyle().HasCharacterStyleName())
3815 {
3816 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3817 if (charDef)
3818 {
3819 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3820 SetBasicStyle(attr);
3821 foundCount ++;
3822 }
3823 }
3824
fe5aa22c
JS
3825 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3826 while (node)
3827 {
3828 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3829 // wxASSERT (para != NULL);
fe5aa22c
JS
3830
3831 if (para)
3832 {
38f833b1
JS
3833 // Combine paragraph and list styles. If there is a list style in the original attributes,
3834 // the current indentation overrides anything else and is used to find the item indentation.
3835 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3836 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3837 // exception as above).
3838 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3839 // So when changing a list style interactively, could retrieve level based on current style, then
3840 // set appropriate indent and apply new style.
41a85215 3841
bbd55ff9
JS
3842 int outline = -1;
3843 int num = -1;
3844 if (para->GetAttributes().HasOutlineLevel())
3845 outline = para->GetAttributes().GetOutlineLevel();
3846 if (para->GetAttributes().HasBulletNumber())
3847 num = para->GetAttributes().GetBulletNumber();
3848
38f833b1
JS
3849 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3850 {
3851 int currentIndent = para->GetAttributes().GetLeftIndent();
3852
3853 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3854 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3855 if (paraDef && !listDef)
3856 {
336d8ae9 3857 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3858 foundCount ++;
3859 }
3860 else if (listDef && !paraDef)
3861 {
3862 // Set overall style defined for the list style definition
336d8ae9 3863 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
3864
3865 // Apply the style for this level
3866 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3867 foundCount ++;
3868 }
3869 else if (listDef && paraDef)
3870 {
3871 // Combines overall list style, style for level, and paragraph style
336d8ae9 3872 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
38f833b1
JS
3873 foundCount ++;
3874 }
3875 }
3876 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3877 {
3878 int currentIndent = para->GetAttributes().GetLeftIndent();
3879
3880 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3881
41a85215 3882 // Overall list definition style
336d8ae9 3883 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
41a85215 3884
38f833b1
JS
3885 // Style for this level
3886 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3887
3888 foundCount ++;
3889 }
3890 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
3891 {
3892 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3893 if (def)
3894 {
336d8ae9 3895 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
fe5aa22c
JS
3896 foundCount ++;
3897 }
3898 }
bbd55ff9
JS
3899
3900 if (outline != -1)
3901 para->GetAttributes().SetOutlineLevel(outline);
3902 if (num != -1)
3903 para->GetAttributes().SetBulletNumber(num);
fe5aa22c
JS
3904 }
3905
3906 node = node->GetNext();
3907 }
3908 return foundCount != 0;
3909}
3910
38f833b1
JS
3911/// Set list style
3912bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3913{
603f702b
JS
3914 wxRichTextBuffer* buffer = GetBuffer();
3915 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3e541562 3916
38f833b1
JS
3917 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3918 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3919 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3920 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 3921
38f833b1
JS
3922 // Current number, if numbering
3923 int n = startFrom;
41a85215 3924
38f833b1
JS
3925 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3926
3927 // If we are associated with a control, make undoable; otherwise, apply immediately
3928 // to the data.
3929
603f702b 3930 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
3931
3932 wxRichTextAction* action = NULL;
3933
3934 if (haveControl && withUndo)
3935 {
603f702b 3936 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 3937 action->SetRange(range);
603f702b 3938 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
3939 }
3940
3941 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3942 while (node)
3943 {
3944 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 3945 // wxASSERT (para != NULL);
38f833b1
JS
3946
3947 if (para && para->GetChildCount() > 0)
3948 {
3949 // Stop searching if we're beyond the range of interest
3950 if (para->GetRange().GetStart() > range.GetEnd())
3951 break;
3952
3953 if (!para->GetRange().IsOutside(range))
3954 {
3955 // We'll be using a copy of the paragraph to make style changes,
3956 // not updating the buffer directly.
3957 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3958
3959 if (haveControl && withUndo)
3960 {
3961 newPara = new wxRichTextParagraph(*para);
3962 action->GetNewParagraphs().AppendChild(newPara);
3963
3964 // Also store the old ones for Undo
3965 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3966 }
3967 else
3968 newPara = para;
41a85215 3969
38f833b1
JS
3970 if (def)
3971 {
3972 int thisIndent = newPara->GetAttributes().GetLeftIndent();
3973 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 3974
38f833b1
JS
3975 // How is numbering going to work?
3976 // If we are renumbering, or numbering for the first time, we need to keep
3977 // track of the number for each level. But we might be simply applying a different
3978 // list style.
3979 // In Word, applying a style to several paragraphs, even if at different levels,
3980 // reverts the level back to the same one. So we could do the same here.
3981 // Renumbering will need to be done when we promote/demote a paragraph.
3982
3983 // Apply the overall list style, and item style for this level
24777478 3984 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 3985 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 3986
d2d0adc7 3987 // Now we need to do numbering
4ce3ebd3
JS
3988 // Preserve the existing list item continuation bullet style, if any
3989 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
3990 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
3991 else
38f833b1 3992 {
4ce3ebd3
JS
3993 // Now we need to do numbering
3994 if (renumber)
3995 {
3996 newPara->GetAttributes().SetBulletNumber(n);
3997 }
41a85215 3998
4ce3ebd3
JS
3999 n ++;
4000 }
38f833b1
JS
4001 }
4002 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4003 {
4004 // if def is NULL, remove list style, applying any associated paragraph style
4005 // to restore the attributes
4006
4007 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4008 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 4009 newPara->GetAttributes().SetBulletText(wxEmptyString);
41a85215 4010
38f833b1 4011 // Eliminate the main list-related attributes
d2d0adc7 4012 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 4013
38f833b1
JS
4014 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4015 {
4016 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4017 if (def)
4018 {
336d8ae9 4019 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
38f833b1
JS
4020 }
4021 }
4022 }
4023 }
4024 }
4025
4026 node = node->GetNext();
4027 }
4028
4029 // Do action, or delay it until end of batch.
4030 if (haveControl && withUndo)
603f702b 4031 buffer->SubmitAction(action);
38f833b1
JS
4032
4033 return true;
4034}
4035
4036bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4037{
603f702b
JS
4038 wxRichTextBuffer* buffer = GetBuffer();
4039 if (buffer && buffer->GetStyleSheet())
38f833b1 4040 {
603f702b 4041 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4042 if (def)
4043 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4044 }
4045 return false;
4046}
4047
4048/// Clear list for given range
4049bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4050{
4051 return SetListStyle(range, NULL, flags);
4052}
4053
4054/// Number/renumber any list elements in the given range
4055bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4056{
4057 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4058}
4059
4060/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4061bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4062 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4063{
603f702b
JS
4064 wxRichTextBuffer* buffer = GetBuffer();
4065 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4066
38f833b1
JS
4067 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4068 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4b6a582b 4069#if wxDEBUG_LEVEL
38f833b1 4070 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 4071#endif
38f833b1
JS
4072
4073 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 4074
38f833b1
JS
4075 // Max number of levels
4076 const int maxLevels = 10;
41a85215 4077
38f833b1
JS
4078 // The level we're looking at now
4079 int currentLevel = -1;
41a85215 4080
38f833b1
JS
4081 // The item number for each level
4082 int levels[maxLevels];
4083 int i;
41a85215 4084
38f833b1
JS
4085 // Reset all numbering
4086 for (i = 0; i < maxLevels; i++)
4087 {
4088 if (startFrom != -1)
d2d0adc7 4089 levels[i] = startFrom-1;
38f833b1 4090 else if (renumber) // start again
d2d0adc7 4091 levels[i] = 0;
38f833b1
JS
4092 else
4093 levels[i] = -1; // start from the number we found, if any
4094 }
41a85215 4095
bb7bbd12 4096#if wxDEBUG_LEVEL
38f833b1 4097 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
bb7bbd12 4098#endif
38f833b1
JS
4099
4100 // If we are associated with a control, make undoable; otherwise, apply immediately
4101 // to the data.
4102
603f702b 4103 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
38f833b1
JS
4104
4105 wxRichTextAction* action = NULL;
4106
4107 if (haveControl && withUndo)
4108 {
603f702b 4109 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
38f833b1 4110 action->SetRange(range);
603f702b 4111 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
38f833b1
JS
4112 }
4113
4114 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4115 while (node)
4116 {
4117 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
603f702b 4118 // wxASSERT (para != NULL);
38f833b1
JS
4119
4120 if (para && para->GetChildCount() > 0)
4121 {
4122 // Stop searching if we're beyond the range of interest
4123 if (para->GetRange().GetStart() > range.GetEnd())
4124 break;
4125
4126 if (!para->GetRange().IsOutside(range))
4127 {
4128 // We'll be using a copy of the paragraph to make style changes,
4129 // not updating the buffer directly.
4130 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4131
4132 if (haveControl && withUndo)
4133 {
4134 newPara = new wxRichTextParagraph(*para);
4135 action->GetNewParagraphs().AppendChild(newPara);
4136
4137 // Also store the old ones for Undo
4138 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4139 }
4140 else
4141 newPara = para;
41a85215 4142
38f833b1
JS
4143 wxRichTextListStyleDefinition* defToUse = def;
4144 if (!defToUse)
4145 {
336d8ae9
VZ
4146 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4147 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
38f833b1 4148 }
41a85215 4149
38f833b1
JS
4150 if (defToUse)
4151 {
4152 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4153 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4154
d2d0adc7
JS
4155 // If we've specified a level to apply to all, change the level.
4156 if (specifiedLevel != -1)
38f833b1 4157 thisLevel = specifiedLevel;
41a85215 4158
38f833b1
JS
4159 // Do promotion if specified
4160 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4161 {
4162 thisLevel = thisLevel - promoteBy;
4163 if (thisLevel < 0)
4164 thisLevel = 0;
4165 if (thisLevel > 9)
4166 thisLevel = 9;
4167 }
41a85215 4168
38f833b1 4169 // Apply the overall list style, and item style for this level
24777478 4170 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
38f833b1 4171 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 4172
4ce3ebd3
JS
4173 // Preserve the existing list item continuation bullet style, if any
4174 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4175 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4176
38f833b1 4177 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 4178
38f833b1
JS
4179 if (currentLevel == -1)
4180 currentLevel = thisLevel;
41a85215 4181
38f833b1
JS
4182 // Same level as before, do nothing except increment level's number afterwards
4183 if (currentLevel == thisLevel)
4184 {
4185 }
4186 // A deeper level: start renumbering all levels after current level
4187 else if (thisLevel > currentLevel)
4188 {
4189 for (i = currentLevel+1; i <= thisLevel; i++)
4190 {
d2d0adc7 4191 levels[i] = 0;
38f833b1
JS
4192 }
4193 currentLevel = thisLevel;
4194 }
4195 else if (thisLevel < currentLevel)
4196 {
4197 currentLevel = thisLevel;
41a85215 4198 }
38f833b1
JS
4199
4200 // Use the current numbering if -1 and we have a bullet number already
4201 if (levels[currentLevel] == -1)
4202 {
4203 if (newPara->GetAttributes().HasBulletNumber())
4204 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4205 else
4206 levels[currentLevel] = 1;
4207 }
d2d0adc7
JS
4208 else
4209 {
4ce3ebd3
JS
4210 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4211 levels[currentLevel] ++;
d2d0adc7 4212 }
41a85215 4213
38f833b1
JS
4214 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4215
d2d0adc7
JS
4216 // Create the bullet text if an outline list
4217 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4218 {
4219 wxString text;
4220 for (i = 0; i <= currentLevel; i++)
4221 {
4222 if (!text.IsEmpty())
4223 text += wxT(".");
4224 text += wxString::Format(wxT("%d"), levels[i]);
4225 }
4226 newPara->GetAttributes().SetBulletText(text);
4227 }
38f833b1
JS
4228 }
4229 }
4230 }
4231
4232 node = node->GetNext();
4233 }
4234
4235 // Do action, or delay it until end of batch.
4236 if (haveControl && withUndo)
603f702b 4237 buffer->SubmitAction(action);
38f833b1
JS
4238
4239 return true;
4240}
4241
4242bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4243{
603f702b
JS
4244 wxRichTextBuffer* buffer = GetBuffer();
4245 if (buffer->GetStyleSheet())
38f833b1
JS
4246 {
4247 wxRichTextListStyleDefinition* def = NULL;
4248 if (!defName.IsEmpty())
603f702b 4249 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4250 return NumberList(range, def, flags, startFrom, specifiedLevel);
4251 }
4252 return false;
4253}
4254
4255/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4256bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4257{
4258 // TODO
4259 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4260 // to NumberList with a flag indicating promotion is required within one of the ranges.
4261 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4262 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4263 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4264 // list position will start from 1.
4265 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4266 // We can end the renumbering at this point.
41a85215 4267
38f833b1 4268 // For now, only renumber within the promotion range.
41a85215 4269
38f833b1
JS
4270 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4271}
4272
4273bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4274{
603f702b
JS
4275 wxRichTextBuffer* buffer = GetBuffer();
4276 if (buffer->GetStyleSheet())
38f833b1
JS
4277 {
4278 wxRichTextListStyleDefinition* def = NULL;
4279 if (!defName.IsEmpty())
603f702b 4280 def = buffer->GetStyleSheet()->FindListStyle(defName);
38f833b1
JS
4281 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4282 }
4283 return false;
4284}
4285
d2d0adc7
JS
4286/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4287/// position of the paragraph that it had to start looking from.
24777478 4288bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
d2d0adc7 4289{
4ce3ebd3
JS
4290 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4291 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4292 {
4293 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4294 if (node)
4295 {
4296 node = node->GetPrevious();
4297 if (node)
4298 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4299 else
4300 previousParagraph = NULL;
4301 }
4302 else
4303 previousParagraph = NULL;
4304 }
4305
d2d0adc7
JS
4306 if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4307 return false;
3e541562 4308
603f702b
JS
4309 wxRichTextBuffer* buffer = GetBuffer();
4310 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
336d8ae9 4311 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
d2d0adc7 4312 {
336d8ae9 4313 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
d2d0adc7
JS
4314 if (def)
4315 {
4316 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4317 // int thisLevel = def->FindLevelForIndent(thisIndent);
3e541562 4318
d2d0adc7
JS
4319 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4320
4321 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4322 if (previousParagraph->GetAttributes().HasBulletName())
4323 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4324 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4325 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
3e541562 4326
d2d0adc7
JS
4327 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4328 attr.SetBulletNumber(nextNumber);
3e541562 4329
d2d0adc7
JS
4330 if (isOutline)
4331 {
4332 wxString text = previousParagraph->GetAttributes().GetBulletText();
4333 if (!text.IsEmpty())
4334 {
4335 int pos = text.Find(wxT('.'), true);
4336 if (pos != wxNOT_FOUND)
4337 {
4338 text = text.Mid(0, text.Length() - pos - 1);
4339 }
4340 else
4341 text = wxEmptyString;
4342 if (!text.IsEmpty())
4343 text += wxT(".");
4344 text += wxString::Format(wxT("%d"), nextNumber);
4345 attr.SetBulletText(text);
4346 }
4347 }
3e541562 4348
d2d0adc7
JS
4349 return true;
4350 }
4351 else
4352 return false;
4353 }
4354 else
4355 return false;
4356}
4357
5d7836c4
JS
4358/*!
4359 * wxRichTextParagraph
4360 * This object represents a single paragraph (or in a straight text editor, a line).
4361 */
4362
603f702b 4363IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
5d7836c4 4364
cfa3b256
JS
4365wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4366
24777478 4367wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
603f702b 4368 wxRichTextCompositeObject(parent)
5d7836c4 4369{
5d7836c4
JS
4370 if (style)
4371 SetAttributes(*style);
4372}
4373
24777478 4374wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
603f702b 4375 wxRichTextCompositeObject(parent)
5d7836c4 4376{
4f32b3cf
JS
4377 if (paraStyle)
4378 SetAttributes(*paraStyle);
5d7836c4 4379
4f32b3cf 4380 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
4381}
4382
4383wxRichTextParagraph::~wxRichTextParagraph()
4384{
4385 ClearLines();
4386}
4387
4388/// Draw the item
8db2e3ef 4389bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
5d7836c4 4390{
603f702b
JS
4391 if (!IsShown())
4392 return true;
4393
4394 // Currently we don't merge these attributes with the parent, but we
4395 // should consider whether we should (e.g. if we set a border colour
4396 // for all paragraphs). But generally box attributes are likely to be
4397 // different for different objects.
4398 wxRect paraRect = GetRect();
24777478 4399 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef
JS
4400 context.ApplyVirtualAttributes(attr, this);
4401
4402 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
fe5aa22c 4403
5d7836c4 4404 // Draw the bullet, if any
4ce3ebd3 4405 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
5d7836c4 4406 {
fe5aa22c 4407 if (attr.GetLeftSubIndent() != 0)
5d7836c4 4408 {
fe5aa22c 4409 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 4410 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 4411
8db2e3ef 4412 wxRichTextAttr bulletAttr(attr);
d2d0adc7 4413
e3eac0ff
JS
4414 // Combine with the font of the first piece of content, if one is specified
4415 if (GetChildren().GetCount() > 0)
4416 {
4417 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
cdaed652 4418 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
e3eac0ff
JS
4419 {
4420 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4421 }
4422 }
4423
d2d0adc7 4424 // Get line height from first line, if any
d3b9f782 4425 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
d2d0adc7
JS
4426
4427 wxPoint linePos;
4428 int lineHeight wxDUMMY_INITIALIZE(0);
4429 if (line)
5d7836c4 4430 {
d2d0adc7
JS
4431 lineHeight = line->GetSize().y;
4432 linePos = line->GetPosition() + GetPosition();
5d7836c4 4433 }
d2d0adc7 4434 else
f089713f 4435 {
f089713f 4436 wxFont font;
44cc96a8
JS
4437 if (bulletAttr.HasFont() && GetBuffer())
4438 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
f089713f
JS
4439 else
4440 font = (*wxNORMAL_FONT);
4441
ecb5fbf1 4442 wxCheckSetFont(dc, font);
f089713f 4443
d2d0adc7
JS
4444 lineHeight = dc.GetCharHeight();
4445 linePos = GetPosition();
4446 linePos.y += spaceBeforePara;
4447 }
f089713f 4448
d2d0adc7 4449 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 4450
d2d0adc7
JS
4451 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4452 {
4453 if (wxRichTextBuffer::GetRenderer())
4454 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4455 }
3e541562
JS
4456 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4457 {
d2d0adc7
JS
4458 if (wxRichTextBuffer::GetRenderer())
4459 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 4460 }
5d7836c4
JS
4461 else
4462 {
4463 wxString bulletText = GetBulletText();
3e541562 4464
d2d0adc7
JS
4465 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4466 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
4467 }
4468 }
4469 }
7fe8059f 4470
5d7836c4
JS
4471 // Draw the range for each line, one object at a time.
4472
4473 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4474 while (node)
4475 {
4476 wxRichTextLine* line = node->GetData();
1e967276 4477 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 4478
5d7836c4
JS
4479 // Lines are specified relative to the paragraph
4480
4481 wxPoint linePosition = line->GetPosition() + GetPosition();
5d7836c4 4482
7051fa41
JS
4483 // Don't draw if off the screen
4484 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
5d7836c4 4485 {
7051fa41
JS
4486 wxPoint objectPosition = linePosition;
4487 int maxDescent = line->GetDescent();
4488
4489 // Loop through objects until we get to the one within range
4490 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3e541562 4491
7051fa41
JS
4492 int i = 0;
4493 while (node2)
5d7836c4 4494 {
7051fa41 4495 wxRichTextObject* child = node2->GetData();
5d7836c4 4496
cdaed652 4497 if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
2f45f554 4498 {
7051fa41
JS
4499 // Draw this part of the line at the correct position
4500 wxRichTextRange objectRange(child->GetRange());
4501 objectRange.LimitTo(lineRange);
4502
4503 wxSize objectSize;
603f702b 4504 if (child->IsTopLevel())
7051fa41 4505 {
603f702b
JS
4506 objectSize = child->GetCachedSize();
4507 objectRange = child->GetOwnRange();
7051fa41
JS
4508 }
4509 else
7051fa41 4510 {
603f702b
JS
4511#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4512 if (i < (int) line->GetObjectSizes().GetCount())
4513 {
4514 objectSize.x = line->GetObjectSizes()[(size_t) i];
4515 }
4516 else
4517#endif
4518 {
4519 int descent = 0;
8db2e3ef 4520 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
603f702b 4521 }
7051fa41 4522 }
5d7836c4 4523
7051fa41
JS
4524 // Use the child object's width, but the whole line's height
4525 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
8db2e3ef 4526 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
5d7836c4 4527
7051fa41
JS
4528 objectPosition.x += objectSize.x;
4529 i ++;
4530 }
4531 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4532 // Can break out of inner loop now since we've passed this line's range
4533 break;
5d7836c4 4534
7051fa41
JS
4535 node2 = node2->GetNext();
4536 }
5d7836c4
JS
4537 }
4538
4539 node = node->GetNext();
7fe8059f 4540 }
5d7836c4
JS
4541
4542 return true;
4543}
4544
4f3d5bc0
JS
4545// Get the range width using partial extents calculated for the whole paragraph.
4546static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4547{
4548 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4549
affbfa1f
JS
4550 if (partialExtents.GetCount() < (size_t) range.GetLength())
4551 return 0;
4552
4f3d5bc0
JS
4553 int leftMostPos = 0;
4554 if (range.GetStart() - para.GetRange().GetStart() > 0)
4555 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4556
4557 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4558
4559 int w = rightMostPos - leftMostPos;
4560
4561 return w;
4562}
4563
5d7836c4 4564/// Lay the item out
8db2e3ef 4565bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
5d7836c4 4566{
cdaed652
VZ
4567 // Deal with floating objects firstly before the normal layout
4568 wxRichTextBuffer* buffer = GetBuffer();
4569 wxASSERT(buffer);
07d4142f 4570 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
cdaed652 4571 wxASSERT(collector);
8db2e3ef 4572 LayoutFloat(dc, context, rect, style, collector);
cdaed652 4573
24777478 4574 wxRichTextAttr attr = GetCombinedAttributes();
8db2e3ef 4575 context.ApplyVirtualAttributes(attr, this);
fe5aa22c 4576
169adfa9
JS
4577 // ClearLines();
4578
5d7836c4 4579 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
4580 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4581 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4582 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4583 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4584 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
4585
4586 int lineSpacing = 0;
4587
4588 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
a1b806b9 4589 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
5d7836c4 4590 {
8f0e4366
JS
4591 wxCheckSetFont(dc, attr.GetFont());
4592 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
5d7836c4
JS
4593 }
4594
5d7836c4
JS
4595 // Start position for each line relative to the paragraph
4596 int startPositionFirstLine = leftIndent;
4597 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4598
4599 // If we have a bullet in this paragraph, the start position for the first line's text
4600 // is actually leftIndent + leftSubIndent.
fe5aa22c 4601 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
4602 startPositionFirstLine = startPositionSubsequentLines;
4603
5d7836c4
JS
4604 long lastEndPos = GetRange().GetStart()-1;
4605 long lastCompletedEndPos = lastEndPos;
4606
4607 int currentWidth = 0;
4608 SetPosition(rect.GetPosition());
4609
4610 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4611 int lineHeight = 0;
4612 int maxWidth = 0;
603f702b 4613 int maxHeight = currentPosition.y;
476a319a 4614 int maxAscent = 0;
5d7836c4 4615 int maxDescent = 0;
5d7836c4 4616 int lineCount = 0;
cdaed652
VZ
4617 int lineAscent = 0;
4618 int lineDescent = 0;
5d7836c4 4619
2f45f554
JS
4620 wxRichTextObjectList::compatibility_iterator node;
4621
4622#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4623 wxUnusedVar(style);
4624 wxArrayInt partialExtents;
4625
4626 wxSize paraSize;
8aab23a1 4627 int paraDescent = 0;
2f45f554
JS
4628
4629 // This calculates the partial text extents
8db2e3ef 4630 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
2f45f554
JS
4631#else
4632 node = m_children.GetFirst();
ecb5fbf1
JS
4633 while (node)
4634 {
4635 wxRichTextObject* child = node->GetData();
4636
603f702b 4637 //child->SetCachedSize(wxDefaultSize);
8db2e3ef 4638 child->Layout(dc, context, rect, style);
ecb5fbf1
JS
4639
4640 node = node->GetNext();
4641 }
31778480
JS
4642#endif
4643
5d7836c4
JS
4644 // Split up lines
4645
4646 // We may need to go back to a previous child, in which case create the new line,
4647 // find the child corresponding to the start position of the string, and
4648 // continue.
4649
603f702b
JS
4650 wxRect availableRect;
4651
ecb5fbf1 4652 node = m_children.GetFirst();
5d7836c4
JS
4653 while (node)
4654 {
4655 wxRichTextObject* child = node->GetData();
4656
cdaed652 4657 // If floating, ignore. We already laid out floats.
603f702b
JS
4658 // Also ignore if empty object, except if we haven't got any
4659 // size yet.
4660 if (child->IsFloating() || !child->IsShown() ||
4661 (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4662 )
affbfa1f
JS
4663 {
4664 node = node->GetNext();
4665 continue;
4666 }
4667
5d7836c4
JS
4668 // If this is e.g. a composite text box, it will need to be laid out itself.
4669 // But if just a text fragment or image, for example, this will
4670 // do nothing. NB: won't we need to set the position after layout?
4671 // since for example if position is dependent on vertical line size, we
4672 // can't tell the position until the size is determined. So possibly introduce
4673 // another layout phase.
4674
5d7836c4
JS
4675 // We may only be looking at part of a child, if we searched back for wrapping
4676 // and found a suitable point some way into the child. So get the size for the fragment
4677 // if necessary.
3e541562 4678
ff76711f
JS
4679 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4680 long lastPosToUse = child->GetRange().GetEnd();
4681 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
3e541562 4682
ff76711f
JS
4683 if (lineBreakInThisObject)
4684 lastPosToUse = nextBreakPos;
5d7836c4
JS
4685
4686 wxSize childSize;
4687 int childDescent = 0;
3e541562 4688
603f702b
JS
4689 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4690 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4691 rect.width - startOffset - rightIndent, rect.height);
4692
4693 if (child->IsTopLevel())
4694 {
4695 wxSize oldSize = child->GetCachedSize();
4696
4697 child->Invalidate(wxRICHTEXT_ALL);
4698 child->SetPosition(wxPoint(0, 0));
4699
4700 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4701 // lays out the object again using the minimum size
4702 // The position will be determined by its location in its line,
4703 // and not by the child's actual position.
8db2e3ef
JS
4704 child->LayoutToBestSize(dc, context, buffer,
4705 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4706
4707 if (oldSize != child->GetCachedSize())
4708 {
4709 partialExtents.Clear();
4710
4711 // Recalculate the partial text extents since the child object changed size
8db2e3ef 4712 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
603f702b
JS
4713 }
4714 }
4715
4716 // Problem: we need to layout composites here for which we need the available width,
4717 // but we can't get the available width without using the float collector which
4718 // needs to know the object height.
4719
ff76711f 4720 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
5d7836c4
JS
4721 {
4722 childSize = child->GetCachedSize();
4723 childDescent = child->GetDescent();
4724 }
4725 else
4f3d5bc0
JS
4726 {
4727#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4728 // Get height only, then the width using the partial extents
8db2e3ef 4729 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4f3d5bc0
JS
4730 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4731#else
8db2e3ef 4732 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
4f3d5bc0
JS
4733#endif
4734 }
ff76711f 4735
603f702b
JS
4736 bool doLoop = true;
4737 int loopIterations = 0;
4738
4739 // If there are nested objects that need to lay themselves out, we have to do this in a
4740 // loop because the height of the object may well depend on the available width.
4741 // And because of floating object positioning, the available width depends on the
4742 // height of the object and whether it will clash with the floating objects.
4743 // So, we see whether the available width changes due to the presence of floating images.
4744 // If it does, then we'll use the new restricted width to find the object height again.
4745 // If this causes another restriction in the available width, we'll try again, until
4746 // either we lose patience or the available width settles down.
4747 do
4748 {
4749 loopIterations ++;
4750
4751 wxRect oldAvailableRect = availableRect;
4752
4753 // Available width depends on the floating objects and the line height.
7c9fdebe 4754 // Note: the floating objects may be placed vertically along the two sides of
603f702b
JS
4755 // buffer, so we may have different available line widths with different
4756 // [startY, endY]. So, we can't determine how wide the available
4757 // space is until we know the exact line height.
a70eb13e
JS
4758 if (childDescent == 0)
4759 {
4760 lineHeight = wxMax(lineHeight, childSize.y);
4761 lineDescent = maxDescent;
4762 lineAscent = maxAscent;
4763 }
4764 else
4765 {
4766 lineDescent = wxMax(childDescent, maxDescent);
4767 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4768 }
4769 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
603f702b
JS
4770 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4771
4772 // Adjust availableRect to the space that is available when taking floating objects into account.
4773
4774 if (floatAvailableRect.x + startOffset > availableRect.x)
4775 {
4776 int newX = floatAvailableRect.x + startOffset;
4777 int newW = availableRect.width - (newX - availableRect.x);
4778 availableRect.x = newX;
4779 availableRect.width = newW;
4780 }
4781
4782 if (floatAvailableRect.width < availableRect.width)
4783 availableRect.width = floatAvailableRect.width;
4784
4785 currentPosition.x = availableRect.x - rect.x;
4786
4787 if (child->IsTopLevel() && loopIterations <= 20)
4788 {
4789 if (availableRect != oldAvailableRect)
4790 {
4791 wxSize oldSize = child->GetCachedSize();
4792
603f702b
JS
4793 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4794 // lays out the object again using the minimum size
4795 child->Invalidate(wxRICHTEXT_ALL);
8db2e3ef
JS
4796 child->LayoutToBestSize(dc, context, buffer,
4797 attr, child->GetAttributes(), availableRect, parentRect, style);
603f702b
JS
4798 childSize = child->GetCachedSize();
4799 childDescent = child->GetDescent();
603f702b
JS
4800
4801 if (oldSize != child->GetCachedSize())
4802 {
4803 partialExtents.Clear();
4804
4805 // Recalculate the partial text extents since the child object changed size
8db2e3ef 4806 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
603f702b 4807 }
cdaed652 4808
603f702b
JS
4809 // Go around the loop finding the available rect for the given floating objects
4810 }
4811 else
4812 doLoop = false;
4813 }
4814 else
4815 doLoop = false;
4816 }
4817 while (doLoop);
cdaed652 4818
20d09da5
JS
4819 if (child->IsTopLevel())
4820 {
4821 // We can move it to the correct position at this point
32423dd8
JS
4822 // TODO: probably need to add margin
4823 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
20d09da5
JS
4824 }
4825
ff76711f
JS
4826 // Cases:
4827 // 1) There was a line break BEFORE the natural break
4828 // 2) There was a line break AFTER the natural break
603f702b
JS
4829 // 3) It's the last line
4830 // 4) The child still fits (carry on) - 'else' clause
5d7836c4 4831
603f702b
JS
4832 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4833 ||
4834 (childSize.x + currentWidth > availableRect.width)
4835 ||
4836 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4837
4838 )
5d7836c4
JS
4839 {
4840 long wrapPosition = 0;
603f702b
JS
4841 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4842 wrapPosition = child->GetRange().GetEnd();
4843 else
5d7836c4
JS
4844
4845 // Find a place to wrap. This may walk back to previous children,
4846 // for example if a word spans several objects.
cdaed652
VZ
4847 // Note: one object must contains only one wxTextAtrr, so the line height will not
4848 // change inside one object. Thus, we can pass the remain line width to the
4849 // FindWrapPosition function.
8db2e3ef 4850 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
5d7836c4
JS
4851 {
4852 // If the function failed, just cut it off at the end of this child.
4853 wrapPosition = child->GetRange().GetEnd();
4854 }
4855
4856 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4857 if (wrapPosition <= lastCompletedEndPos)
4858 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4859
603f702b
JS
4860 // Line end position shouldn't be the same as the end, or greater.
4861 if (wrapPosition >= GetRange().GetEnd())
a8a15de6 4862 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
603f702b 4863
5d7836c4 4864 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 4865
5d7836c4
JS
4866 // Let's find the actual size of the current line now
4867 wxSize actualSize;
4868 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4f3d5bc0 4869
a70eb13e 4870 childDescent = 0;
4ab8a5e2 4871
4f3d5bc0 4872#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
603f702b
JS
4873 if (!child->IsEmpty())
4874 {
4875 // Get height only, then the width using the partial extents
8db2e3ef 4876 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
603f702b
JS
4877 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4878 }
4879 else
4f3d5bc0 4880#endif
8db2e3ef 4881 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
4f3d5bc0 4882
5d7836c4 4883 currentWidth = actualSize.x;
a70eb13e
JS
4884
4885 // The descent for the whole line at this point, is the correct max descent
4886 maxDescent = childDescent;
4887 // Maximum ascent
4888 maxAscent = actualSize.y-childDescent;
4889
4890 // lineHeight is given by the height for the whole line, since it will
4891 // take into account ascend/descend.
4892 lineHeight = actualSize.y;
7fe8059f 4893
07d4142f 4894 if (lineHeight == 0 && buffer)
603f702b 4895 {
07d4142f 4896 wxFont font(buffer->GetFontTable().FindFont(attr));
603f702b
JS
4897 wxCheckSetFont(dc, font);
4898 lineHeight = dc.GetCharHeight();
4899 }
4900
4901 if (maxDescent == 0)
4902 {
4903 int w, h;
4904 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
4905 }
4906
5d7836c4 4907 // Add a new line
1e967276 4908 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 4909
1e967276
JS
4910 // Set relative range so we won't have to change line ranges when paragraphs are moved
4911 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
4912 line->SetPosition(currentPosition);
4913 line->SetSize(wxSize(currentWidth, lineHeight));
4914 line->SetDescent(maxDescent);
4915
603f702b
JS
4916 maxHeight = currentPosition.y + lineHeight;
4917
5d7836c4
JS
4918 // Now move down a line. TODO: add margins, spacing
4919 currentPosition.y += lineHeight;
4920 currentPosition.y += lineSpacing;
5d7836c4 4921 maxDescent = 0;
476a319a 4922 maxAscent = 0;
603f702b
JS
4923 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4924 currentWidth = 0;
7fe8059f 4925
5d7836c4
JS
4926 lineCount ++;
4927
a70eb13e 4928 // TODO: account for zero-length objects
603f702b 4929 // wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 4930
5d7836c4
JS
4931 lastEndPos = wrapPosition;
4932 lastCompletedEndPos = lastEndPos;
4933
4934 lineHeight = 0;
4935
603f702b
JS
4936 if (wrapPosition < GetRange().GetEnd()-1)
4937 {
4938 // May need to set the node back to a previous one, due to searching back in wrapping
4939 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
4940 if (childAfterWrapPosition)
4941 node = m_children.Find(childAfterWrapPosition);
4942 else
4943 node = node->GetNext();
4944 }
5d7836c4
JS
4945 else
4946 node = node->GetNext();
603f702b
JS
4947
4948 // Apply paragraph styles such as alignment to the wrapped line
4949 ApplyParagraphStyle(line, attr, availableRect, dc);
5d7836c4
JS
4950 }
4951 else
4952 {
4953 // We still fit, so don't add a line, and keep going
4954 currentWidth += childSize.x;
a70eb13e
JS
4955
4956 if (childDescent == 0)
4957 {
4958 // An object with a zero descend value wants to take up the whole
4959 // height regardless of baseline
4960 lineHeight = wxMax(lineHeight, childSize.y);
4961 }
4962 else
4963 {
4964 maxDescent = wxMax(childDescent, maxDescent);
4965 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
4966 }
4967
4968 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5d7836c4 4969
603f702b 4970 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5d7836c4
JS
4971 lastEndPos = child->GetRange().GetEnd();
4972
4973 node = node->GetNext();
4974 }
4975 }
4976
07d4142f 4977 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5d7836c4 4978
1e967276
JS
4979 // Remove remaining unused line objects, if any
4980 ClearUnusedLines(lineCount);
4981
603f702b
JS
4982 // We need to add back the margins etc.
4983 {
4984 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4985 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 4986 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4987 SetCachedSize(marginRect.GetSize());
4988 }
4989
4990 // The maximum size is the length of the paragraph stretched out into a line.
4991 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4992 // this size. TODO: take into account line breaks.
4993 {
4994 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
031b5b0c 4995 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
8db2e3ef 4996 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
4997 SetMaxSize(marginRect.GetSize());
4998 }
4999
5000 // Find the greatest minimum size. Currently we only look at non-text objects,
5001 // which isn't ideal but it would be slow to find the maximum word width to
5002 // use as the minimum.
5003 {
5004 int minWidth = 0;
5005 node = m_children.GetFirst();
5006 while (node)
5007 {
5008 wxRichTextObject* child = node->GetData();
5009
5010 // If floating, ignore. We already laid out floats.
5011 // Also ignore if empty object, except if we haven't got any
5012 // size yet.
345c78ca 5013 if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
603f702b
JS
5014 {
5015 if (child->GetCachedSize().x > minWidth)
5016 minWidth = child->GetMinSize().x;
5017 }
5018 node = node->GetNext();
5019 }
5d7836c4 5020
603f702b
JS
5021 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5022 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
8db2e3ef 5023 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
5024 SetMinSize(marginRect.GetSize());
5025 }
5d7836c4 5026
2f45f554
JS
5027#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5028#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5029 // Use the text extents to calculate the size of each fragment in each line
5030 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5031 while (lineNode)
5032 {
5033 wxRichTextLine* line = lineNode->GetData();
5034 wxRichTextRange lineRange = line->GetAbsoluteRange();
5035
5036 // Loop through objects until we get to the one within range
5037 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5038
5039 while (node2)
5040 {
5041 wxRichTextObject* child = node2->GetData();
5042
affbfa1f 5043 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
2f45f554
JS
5044 {
5045 wxRichTextRange rangeToUse = lineRange;
5046 rangeToUse.LimitTo(child->GetRange());
5047
5048 // Find the size of the child from the text extents, and store in an array
5049 // for drawing later
5050 int left = 0;
5051 if (rangeToUse.GetStart() > GetRange().GetStart())
5052 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5053 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5054 int sz = right - left;
5055 line->GetObjectSizes().Add(sz);
5056 }
5057 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5058 // Can break out of inner loop now since we've passed this line's range
5059 break;
5060
5061 node2 = node2->GetNext();
5062 }
5063
5064 lineNode = lineNode->GetNext();
5065 }
5066#endif
5067#endif
5068
5d7836c4
JS
5069 return true;
5070}
5071
603f702b
JS
5072/// Apply paragraph styles, such as centering, to wrapped lines
5073/// TODO: take into account box attributes, possibly
5074void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5075{
5076 if (!attr.HasAlignment())
5077 return;
5078
5079 wxPoint pos = line->GetPosition();
32423dd8 5080 wxPoint originalPos = pos;
603f702b
JS
5081 wxSize size = line->GetSize();
5082
5083 // centering, right-justification
8db2e3ef 5084 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
603f702b
JS
5085 {
5086 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5087 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5088 line->SetPosition(pos);
5089 }
8db2e3ef 5090 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
603f702b
JS
5091 {
5092 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5093 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5094 line->SetPosition(pos);
5095 }
32423dd8
JS
5096
5097 if (pos != originalPos)
5098 {
5099 wxPoint inc = pos - originalPos;
5100
5101 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5102
5103 while (node)
5104 {
5105 wxRichTextObject* child = node->GetData();
5106 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5107 child->Move(child->GetPosition() + inc);
5108
5109 node = node->GetNext();
5110 }
5111 }
603f702b 5112}
5d7836c4
JS
5113
5114/// Insert text at the given position
5115bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5116{
5117 wxRichTextObject* childToUse = NULL;
09f14108 5118 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
5119
5120 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5121 while (node)
5122 {
5123 wxRichTextObject* child = node->GetData();
5124 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5125 {
5126 childToUse = child;
5127 nodeToUse = node;
5128 break;
5129 }
5130
5131 node = node->GetNext();
5132 }
5133
5134 if (childToUse)
5135 {
5136 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5137 if (textObject)
5138 {
5139 int posInString = pos - textObject->GetRange().GetStart();
5140
5141 wxString newText = textObject->GetText().Mid(0, posInString) +
5142 text + textObject->GetText().Mid(posInString);
5143 textObject->SetText(newText);
5144
28f92d74 5145 int textLength = text.length();
5d7836c4
JS
5146
5147 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5148 textObject->GetRange().GetEnd() + textLength));
5149
5150 // Increment the end range of subsequent fragments in this paragraph.
5151 // We'll set the paragraph range itself at a higher level.
5152
5153 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5154 while (node)
5155 {
5156 wxRichTextObject* child = node->GetData();
5157 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5158 textObject->GetRange().GetEnd() + textLength));
7fe8059f 5159
5d7836c4
JS
5160 node = node->GetNext();
5161 }
5162
5163 return true;
5164 }
5165 else
5166 {
5167 // TODO: if not a text object, insert at closest position, e.g. in front of it
5168 }
5169 }
5170 else
5171 {
5172 // Add at end.
5173 // Don't pass parent initially to suppress auto-setting of parent range.
5174 // We'll do that at a higher level.
5175 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5176
5177 AppendChild(textObject);
5178 return true;
5179 }
5180
5181 return false;
5182}
5183
5184void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5185{
bec80f4f 5186 wxRichTextCompositeObject::Copy(obj);
5d7836c4
JS
5187}
5188
5189/// Clear the cached lines
5190void wxRichTextParagraph::ClearLines()
5191{
5192 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5193}
5194
5195/// Get/set the object size for the given range. Returns false if the range
5196/// is invalid for this object.
8db2e3ef 5197bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
5198{
5199 if (!range.IsWithin(GetRange()))
5200 return false;
5201
5202 if (flags & wxRICHTEXT_UNFORMATTED)
5203 {
5204 // Just use unformatted data, assume no line breaks
5d7836c4
JS
5205 wxSize sz;
5206
31778480
JS
5207 wxArrayInt childExtents;
5208 wxArrayInt* p;
5209 if (partialExtents)
5210 p = & childExtents;
5211 else
5212 p = NULL;
5213
a70eb13e
JS
5214 int maxDescent = 0;
5215 int maxAscent = 0;
5216 int maxLineHeight = 0;
5217
5d7836c4
JS
5218 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5219 while (node)
5220 {
5221 wxRichTextObject* child = node->GetData();
5222 if (!child->GetRange().IsOutside(range))
5223 {
cdaed652
VZ
5224 // Floating objects have a zero size within the paragraph.
5225 if (child->IsFloating())
5226 {
5227 if (partialExtents)
5228 {
5229 int lastSize;
5230 if (partialExtents->GetCount() > 0)
5231 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5232 else
5233 lastSize = 0;
5234
5235 partialExtents->Add(0 /* zero size */ + lastSize);
5236 }
5237 }
5238 else
5239 {
603f702b 5240 wxSize childSize;
4f3d5bc0 5241
603f702b
JS
5242 wxRichTextRange rangeToUse = range;
5243 rangeToUse.LimitTo(child->GetRange());
603f702b 5244 int childDescent = 0;
31778480 5245
7c9fdebe 5246 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
603f702b
JS
5247 // but it's only going to be used after caching has taken place.
5248 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
2f45f554 5249 {
603f702b
JS
5250 childDescent = child->GetDescent();
5251 childSize = child->GetCachedSize();
2f45f554 5252
a70eb13e
JS
5253 if (childDescent == 0)
5254 {
5255 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5256 }
5257 else
5258 {
5259 maxDescent = wxMax(maxDescent, childDescent);
5260 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5261 }
5262
5263 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5264
5265 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5266 sz.x += childSize.x;
a70eb13e 5267 descent = maxDescent;
603f702b
JS
5268 }
5269 else if (child->IsTopLevel())
31778480 5270 {
603f702b
JS
5271 childDescent = child->GetDescent();
5272 childSize = child->GetCachedSize();
31778480 5273
a70eb13e
JS
5274 if (childDescent == 0)
5275 {
5276 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5277 }
5278 else
5279 {
5280 maxDescent = wxMax(maxDescent, childDescent);
5281 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5282 }
5283
5284 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5285
5286 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5287 sz.x += childSize.x;
a70eb13e
JS
5288 descent = maxDescent;
5289
5290 // FIXME: this won't change the original values.
5291 // Should we be calling GetRangeSize above instead of using cached values?
5292#if 0
603f702b 5293 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
31778480 5294 {
603f702b
JS
5295 child->SetCachedSize(childSize);
5296 child->SetDescent(childDescent);
31778480 5297 }
a70eb13e 5298#endif
31778480 5299
603f702b
JS
5300 if (partialExtents)
5301 {
5302 int lastSize;
5303 if (partialExtents->GetCount() > 0)
5304 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5305 else
5306 lastSize = 0;
5307
5308 partialExtents->Add(childSize.x + lastSize);
5309 }
5310 }
8db2e3ef 5311 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
603f702b 5312 {
a70eb13e
JS
5313 if (childDescent == 0)
5314 {
5315 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5316 }
5317 else
5318 {
5319 maxDescent = wxMax(maxDescent, childDescent);
5320 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5321 }
5322
5323 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5324
5325 sz.y = wxMax(sz.y, maxLineHeight);
603f702b 5326 sz.x += childSize.x;
a70eb13e 5327 descent = maxDescent;
603f702b
JS
5328
5329 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5330 {
5331 child->SetCachedSize(childSize);
5332 child->SetDescent(childDescent);
5333 }
5334
5335 if (partialExtents)
5336 {
5337 int lastSize;
5338 if (partialExtents->GetCount() > 0)
5339 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5340 else
5341 lastSize = 0;
5342
5343 size_t i;
5344 for (i = 0; i < childExtents.GetCount(); i++)
5345 {
5346 partialExtents->Add(childExtents[i] + lastSize);
5347 }
5348 }
5349 }
5350 }
5351
5352 if (p)
5353 p->Clear();
5d7836c4
JS
5354 }
5355
5356 node = node->GetNext();
5357 }
5358 size = sz;
5359 }
5360 else
5361 {
5362 // Use formatted data, with line breaks
5363 wxSize sz;
5364
5365 // We're going to loop through each line, and then for each line,
5366 // call GetRangeSize for the fragment that comprises that line.
5367 // Only we have to do that multiple times within the line, because
5368 // the line may be broken into pieces. For now ignore line break commands
5369 // (so we can assume that getting the unformatted size for a fragment
5370 // within a line is the actual size)
5371
5372 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5373 while (node)
5374 {
5375 wxRichTextLine* line = node->GetData();
1e967276
JS
5376 wxRichTextRange lineRange = line->GetAbsoluteRange();
5377 if (!lineRange.IsOutside(range))
5d7836c4 5378 {
a70eb13e
JS
5379 int maxDescent = 0;
5380 int maxAscent = 0;
5381 int maxLineHeight = 0;
5382 int maxLineWidth = 0;
7fe8059f 5383
5d7836c4
JS
5384 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5385 while (node2)
5386 {
5387 wxRichTextObject* child = node2->GetData();
7fe8059f 5388
cdaed652 5389 if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
5d7836c4 5390 {
1e967276 5391 wxRichTextRange rangeToUse = lineRange;
5d7836c4 5392 rangeToUse.LimitTo(child->GetRange());
603f702b
JS
5393 if (child->IsTopLevel())
5394 rangeToUse = child->GetOwnRange();
7fe8059f 5395
5d7836c4
JS
5396 wxSize childSize;
5397 int childDescent = 0;
8db2e3ef 5398 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
5d7836c4 5399 {
a70eb13e
JS
5400 if (childDescent == 0)
5401 {
5402 // Assume that if descent is zero, this child can occupy the full line height
5403 // and does not need space for the line's maximum descent. So we influence
5404 // the overall max line height only.
5405 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5406 }
5407 else
5408 {
5409 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5410 maxDescent = wxMax(maxAscent, childDescent);
5411 }
5412 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5413 maxLineWidth += childSize.x;
5d7836c4 5414 }
5d7836c4 5415 }
7fe8059f 5416
5d7836c4
JS
5417 node2 = node2->GetNext();
5418 }
5419
a70eb13e
JS
5420 descent = wxMax(descent, maxDescent);
5421
5d7836c4 5422 // Increase size by a line (TODO: paragraph spacing)
a70eb13e
JS
5423 sz.y += maxLineHeight;
5424 sz.x = wxMax(sz.x, maxLineWidth);
5d7836c4
JS
5425 }
5426 node = node->GetNext();
5427 }
5428 size = sz;
5429 }
5430 return true;
5431}
5432
5433/// Finds the absolute position and row height for the given character position
8db2e3ef 5434bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5d7836c4
JS
5435{
5436 if (index == -1)
5437 {
5438 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5439 if (line)
5440 *height = line->GetSize().y;
5441 else
5442 *height = dc.GetCharHeight();
5443
5444 // -1 means 'the start of the buffer'.
5445 pt = GetPosition();
5446 if (line)
5447 pt = pt + line->GetPosition();
5448
5d7836c4
JS
5449 return true;
5450 }
5451
5452 // The final position in a paragraph is taken to mean the position
5453 // at the start of the next paragraph.
5454 if (index == GetRange().GetEnd())
5455 {
5456 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5457 wxASSERT( parent != NULL );
5458
5459 // Find the height at the next paragraph, if any
5460 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5461 if (line)
5462 {
5463 *height = line->GetSize().y;
5464 pt = line->GetAbsolutePosition();
5465 }
5466 else
5467 {
5468 *height = dc.GetCharHeight();
5469 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5470 pt = wxPoint(indent, GetCachedSize().y);
5471 }
5472
5473 return true;
5474 }
5475
5476 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5477 return false;
5478
5479 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5480 while (node)
5481 {
5482 wxRichTextLine* line = node->GetData();
1e967276
JS
5483 wxRichTextRange lineRange = line->GetAbsoluteRange();
5484 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
5485 {
5486 // If this is the last point in the line, and we're forcing the
5487 // returned value to be the start of the next line, do the required
5488 // thing.
1e967276 5489 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
5490 {
5491 if (node->GetNext())
5492 {
5493 wxRichTextLine* nextLine = node->GetNext()->GetData();
5494 *height = nextLine->GetSize().y;
5495 pt = nextLine->GetAbsolutePosition();
5496 return true;
5497 }
5498 }
5499
5500 pt.y = line->GetPosition().y + GetPosition().y;
5501
1e967276 5502 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
5503 wxSize rangeSize;
5504 int descent = 0;
5505
5506 // We find the size of the line up to this point,
5507 // then we can add this size to the line start position and
5508 // paragraph start position to find the actual position.
5509
8db2e3ef 5510 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
5511 {
5512 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5513 *height = line->GetSize().y;
5514
5515 return true;
5516 }
5517
5518 }
5519
5520 node = node->GetNext();
5521 }
5522
5523 return false;
5524}
5525
5526/// Hit-testing: returns a flag indicating hit test details, plus
5527/// information about position
8db2e3ef 5528int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5d7836c4 5529{
603f702b
JS
5530 if (!IsShown())
5531 return wxRICHTEXT_HITTEST_NONE;
5532
5533 // If we're in the top-level container, then we can return
5534 // a suitable hit test code even if the point is outside the container area,
5535 // so that we can position the caret sensibly even if we don't
5536 // click on valid content. If we're not at the top-level, and the point
5537 // is not within this paragraph object, then we don't want to stop more
5538 // precise hit-testing from working prematurely, so return immediately.
5539 // NEW STRATEGY: use the parent boundary to test whether we're in the
5540 // right region, not the paragraph, since the paragraph may be positioned
5541 // some way in from where the user clicks.
5542 {
5543 long tmpPos;
5544 wxRichTextObject* tempObj, *tempContextObj;
8db2e3ef 5545 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
603f702b
JS
5546 return wxRICHTEXT_HITTEST_NONE;
5547 }
5548
5549 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5550 while (objNode)
5551 {
5552 wxRichTextObject* child = objNode->GetData();
7c9fdebe
JS
5553 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5554 // and also, if this seems composite but actually is marked as atomic,
5555 // don't recurse.
5556 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5557 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
603f702b
JS
5558 {
5559 {
8db2e3ef 5560 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
603f702b
JS
5561 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5562 return hitTest;
5563 }
5564 }
5565
5566 objNode = objNode->GetNext();
5567 }
5568
5d7836c4
JS
5569 wxPoint paraPos = GetPosition();
5570
5571 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5572 while (node)
5573 {
5574 wxRichTextLine* line = node->GetData();
5575 wxPoint linePos = paraPos + line->GetPosition();
5576 wxSize lineSize = line->GetSize();
1e967276 5577 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4 5578
62381daa 5579 if (pt.y <= linePos.y + lineSize.y)
5d7836c4
JS
5580 {
5581 if (pt.x < linePos.x)
5582 {
1e967276 5583 textPosition = lineRange.GetStart();
603f702b
JS
5584 *obj = FindObjectAtPosition(textPosition);
5585 *contextObj = GetContainer();
f262b25c 5586 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5587 }
5588 else if (pt.x >= (linePos.x + lineSize.x))
5589 {
1e967276 5590 textPosition = lineRange.GetEnd();
603f702b
JS
5591 *obj = FindObjectAtPosition(textPosition);
5592 *contextObj = GetContainer();
f262b25c 5593 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5d7836c4
JS
5594 }
5595 else
5596 {
2f45f554
JS
5597#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5598 wxArrayInt partialExtents;
5599
5600 wxSize paraSize;
5601 int paraDescent;
5602
5603 // This calculates the partial text extents
8db2e3ef 5604 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
2f45f554
JS
5605
5606 int lastX = linePos.x;
5607 size_t i;
5608 for (i = 0; i < partialExtents.GetCount(); i++)
5609 {
5610 int nextX = partialExtents[i] + linePos.x;
5611
5612 if (pt.x >= lastX && pt.x <= nextX)
5613 {
5614 textPosition = i + lineRange.GetStart(); // minus 1?
5615
603f702b
JS
5616 *obj = FindObjectAtPosition(textPosition);
5617 *contextObj = GetContainer();
5618
2f45f554
JS
5619 // So now we know it's between i-1 and i.
5620 // Let's see if we can be more precise about
5621 // which side of the position it's on.
5622
cdaed652 5623 int midPoint = (nextX + lastX)/2;
2f45f554
JS
5624 if (pt.x >= midPoint)
5625 return wxRICHTEXT_HITTEST_AFTER;
5626 else
5627 return wxRICHTEXT_HITTEST_BEFORE;
5628 }
5629
5630 lastX = nextX;
5631 }
5632#else
5d7836c4
JS
5633 long i;
5634 int lastX = linePos.x;
1e967276 5635 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
5636 {
5637 wxSize childSize;
5638 int descent = 0;
7fe8059f 5639
1e967276 5640 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 5641
8db2e3ef 5642 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
5643
5644 int nextX = childSize.x + linePos.x;
5645
5646 if (pt.x >= lastX && pt.x <= nextX)
5647 {
5648 textPosition = i;
5649
603f702b
JS
5650 *obj = FindObjectAtPosition(textPosition);
5651 *contextObj = GetContainer();
5652
5d7836c4
JS
5653 // So now we know it's between i-1 and i.
5654 // Let's see if we can be more precise about
5655 // which side of the position it's on.
5656
cdaed652 5657 int midPoint = (nextX + lastX)/2;
5d7836c4
JS
5658 if (pt.x >= midPoint)
5659 return wxRICHTEXT_HITTEST_AFTER;
5660 else
5661 return wxRICHTEXT_HITTEST_BEFORE;
5662 }
5663 else
5664 {
5665 lastX = nextX;
5666 }
5667 }
2f45f554 5668#endif
5d7836c4
JS
5669 }
5670 }
7fe8059f 5671
5d7836c4
JS
5672 node = node->GetNext();
5673 }
5674
5675 return wxRICHTEXT_HITTEST_NONE;
5676}
5677
5678/// Split an object at this position if necessary, and return
5679/// the previous object, or NULL if inserting at beginning.
5680wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5681{
5682 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5683 while (node)
5684 {
5685 wxRichTextObject* child = node->GetData();
5686
5687 if (pos == child->GetRange().GetStart())
5688 {
5689 if (previousObject)
4d551ad5
JS
5690 {
5691 if (node->GetPrevious())
5692 *previousObject = node->GetPrevious()->GetData();
5693 else
5694 *previousObject = NULL;
5695 }
5d7836c4
JS
5696
5697 return child;
5698 }
5699
5700 if (child->GetRange().Contains(pos))
5701 {
5702 // This should create a new object, transferring part of
5703 // the content to the old object and the rest to the new object.
5704 wxRichTextObject* newObject = child->DoSplit(pos);
5705
5706 // If we couldn't split this object, just insert in front of it.
5707 if (!newObject)
5708 {
5709 // Maybe this is an empty string, try the next one
5710 // return child;
5711 }
5712 else
5713 {
5714 // Insert the new object after 'child'
5715 if (node->GetNext())
5716 m_children.Insert(node->GetNext(), newObject);
5717 else
5718 m_children.Append(newObject);
5719 newObject->SetParent(this);
5720
5721 if (previousObject)
5722 *previousObject = child;
5723
5724 return newObject;
5725 }
5726 }
5727
5728 node = node->GetNext();
5729 }
5730 if (previousObject)
5731 *previousObject = NULL;
5732 return NULL;
5733}
5734
5735/// Move content to a list from obj on
5736void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5737{
5738 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5739 while (node)
5740 {
5741 wxRichTextObject* child = node->GetData();
5742 list.Append(child);
5743
5744 wxRichTextObjectList::compatibility_iterator oldNode = node;
5745
5746 node = node->GetNext();
5747
5748 m_children.DeleteNode(oldNode);
5749 }
5750}
5751
5752/// Add content back from list
5753void wxRichTextParagraph::MoveFromList(wxList& list)
5754{
09f14108 5755 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5756 {
5757 AppendChild((wxRichTextObject*) node->GetData());
5758 }
5759}
5760
5761/// Calculate range
5762void wxRichTextParagraph::CalculateRange(long start, long& end)
5763{
5764 wxRichTextCompositeObject::CalculateRange(start, end);
5765
5766 // Add one for end of paragraph
5767 end ++;
5768
5769 m_range.SetRange(start, end);
5770}
5771
5772/// Find the object at the given position
5773wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5774{
5775 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5776 while (node)
5777 {
5778 wxRichTextObject* obj = node->GetData();
603f702b
JS
5779 if (obj->GetRange().Contains(position) ||
5780 obj->GetRange().GetStart() == position ||
5781 obj->GetRange().GetEnd() == position)
5d7836c4 5782 return obj;
7fe8059f 5783
5d7836c4
JS
5784 node = node->GetNext();
5785 }
5786 return NULL;
5787}
5788
5789/// Get the plain text searching from the start or end of the range.
5790/// The resulting string may be shorter than the range given.
5791bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5792{
5793 text = wxEmptyString;
5794
5795 if (fromStart)
5796 {
5797 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5798 while (node)
5799 {
5800 wxRichTextObject* obj = node->GetData();
5801 if (!obj->GetRange().IsOutside(range))
5802 {
5803 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5804 if (textObj)
5805 {
5806 text += textObj->GetTextForRange(range);
5807 }
5808 else
043c0d58
JS
5809 {
5810 text += wxT(" ");
5811 }
5d7836c4
JS
5812 }
5813
5814 node = node->GetNext();
5815 }
5816 }
5817 else
5818 {
5819 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5820 while (node)
5821 {
5822 wxRichTextObject* obj = node->GetData();
5823 if (!obj->GetRange().IsOutside(range))
5824 {
5825 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5826 if (textObj)
5827 {
5828 text = textObj->GetTextForRange(range) + text;
5829 }
5830 else
043c0d58
JS
5831 {
5832 text = wxT(" ") + text;
5833 }
5d7836c4
JS
5834 }
5835
5836 node = node->GetPrevious();
5837 }
5838 }
5839
5840 return true;
5841}
5842
5843/// Find a suitable wrap position.
8db2e3ef 5844bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5d7836c4 5845{
72945e24
JS
5846 if (range.GetLength() <= 0)
5847 return false;
5848
5d7836c4
JS
5849 // Find the first position where the line exceeds the available space.
5850 wxSize sz;
5d7836c4 5851 long breakPosition = range.GetEnd();
ecb5fbf1 5852
31778480
JS
5853#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5854 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5d7836c4 5855 {
31778480 5856 int widthBefore;
5d7836c4 5857
31778480
JS
5858 if (range.GetStart() > GetRange().GetStart())
5859 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5860 else
5861 widthBefore = 0;
5862
5863 size_t i;
43a0d1e1 5864 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5d7836c4 5865 {
31778480 5866 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
ecb5fbf1 5867
72945e24 5868 if (widthFromStartOfThisRange > availableSpace)
ecb5fbf1 5869 {
31778480
JS
5870 breakPosition = i-1;
5871 break;
ecb5fbf1 5872 }
5d7836c4 5873 }
31778480
JS
5874 }
5875 else
5876#endif
5877 {
5878 // Binary chop for speed
5879 long minPos = range.GetStart();
5880 long maxPos = range.GetEnd();
5881 while (true)
ecb5fbf1 5882 {
31778480
JS
5883 if (minPos == maxPos)
5884 {
5885 int descent = 0;
8db2e3ef 5886 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
ecb5fbf1 5887
31778480
JS
5888 if (sz.x > availableSpace)
5889 breakPosition = minPos - 1;
5890 break;
5891 }
5892 else if ((maxPos - minPos) == 1)
ecb5fbf1 5893 {
31778480 5894 int descent = 0;
8db2e3ef 5895 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5896
5897 if (sz.x > availableSpace)
5898 breakPosition = minPos - 1;
5899 else
5900 {
8db2e3ef 5901 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5902 if (sz.x > availableSpace)
5903 breakPosition = maxPos-1;
5904 }
5905 break;
ecb5fbf1
JS
5906 }
5907 else
5908 {
31778480
JS
5909 long nextPos = minPos + ((maxPos - minPos) / 2);
5910
5911 int descent = 0;
8db2e3ef 5912 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
31778480
JS
5913
5914 if (sz.x > availableSpace)
5915 {
5916 maxPos = nextPos;
5917 }
5918 else
5919 {
5920 minPos = nextPos;
5921 }
ecb5fbf1
JS
5922 }
5923 }
5d7836c4
JS
5924 }
5925
5926 // Now we know the last position on the line.
5927 // Let's try to find a word break.
5928
5929 wxString plainText;
5930 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
5931 {
ff76711f
JS
5932 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
5933 if (newLinePos != wxNOT_FOUND)
5d7836c4 5934 {
ff76711f
JS
5935 breakPosition = wxMax(0, range.GetStart() + newLinePos);
5936 }
5937 else
5938 {
5939 int spacePos = plainText.Find(wxT(' '), true);
31002e44
JS
5940 int tabPos = plainText.Find(wxT('\t'), true);
5941 int pos = wxMax(spacePos, tabPos);
5942 if (pos != wxNOT_FOUND)
ff76711f 5943 {
31002e44 5944 int positionsFromEndOfString = plainText.length() - pos - 1;
ff76711f
JS
5945 breakPosition = breakPosition - positionsFromEndOfString;
5946 }
5d7836c4
JS
5947 }
5948 }
5949
5950 wrapPosition = breakPosition;
5951
5952 return true;
5953}
5954
5955/// Get the bullet text for this paragraph.
5956wxString wxRichTextParagraph::GetBulletText()
5957{
5958 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
5959 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
5960 return wxEmptyString;
5961
5962 int number = GetAttributes().GetBulletNumber();
5963
5964 wxString text;
d2d0adc7 5965 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
5966 {
5967 text.Printf(wxT("%d"), number);
5968 }
5969 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
5970 {
5971 // TODO: Unicode, and also check if number > 26
5972 text.Printf(wxT("%c"), (wxChar) (number+64));
5973 }
5974 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
5975 {
5976 // TODO: Unicode, and also check if number > 26
5977 text.Printf(wxT("%c"), (wxChar) (number+96));
5978 }
5979 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
5980 {
59509217 5981 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
5982 }
5983 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
5984 {
59509217
JS
5985 text = wxRichTextDecimalToRoman(number);
5986 text.MakeLower();
5d7836c4
JS
5987 }
5988 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
5989 {
d2d0adc7
JS
5990 text = GetAttributes().GetBulletText();
5991 }
3e541562 5992
d2d0adc7
JS
5993 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
5994 {
5995 // The outline style relies on the text being computed statically,
5996 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5997 // should be stored in the attributes; if not, just use the number for this
5998 // level, as previously computed.
5999 if (!GetAttributes().GetBulletText().IsEmpty())
6000 text = GetAttributes().GetBulletText();
5d7836c4
JS
6001 }
6002
6003 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6004 {
6005 text = wxT("(") + text + wxT(")");
6006 }
d2d0adc7
JS
6007 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6008 {
6009 text = text + wxT(")");
6010 }
6011
5d7836c4
JS
6012 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6013 {
6014 text += wxT(".");
6015 }
6016
6017 return text;
6018}
6019
1e967276
JS
6020/// Allocate or reuse a line object
6021wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6022{
6023 if (pos < (int) m_cachedLines.GetCount())
6024 {
6025 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6026 line->Init(this);
6027 return line;
6028 }
6029 else
6030 {
6031 wxRichTextLine* line = new wxRichTextLine(this);
6032 m_cachedLines.Append(line);
6033 return line;
6034 }
6035}
6036
6037/// Clear remaining unused line objects, if any
6038bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6039{
6040 int cachedLineCount = m_cachedLines.GetCount();
6041 if ((int) cachedLineCount > lineCount)
6042 {
6043 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6044 {
6045 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6046 wxRichTextLine* line = node->GetData();
6047 m_cachedLines.Erase(node);
6048 delete line;
6049 }
6050 }
6051 return true;
6052}
6053
fe5aa22c
JS
6054/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6055/// retrieve the actual style.
603f702b 6056wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
fe5aa22c 6057{
24777478 6058 wxRichTextAttr attr;
603f702b 6059 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6060 if (buf)
6061 {
6062 attr = buf->GetBasicStyle();
603f702b
JS
6063 if (!includingBoxAttr)
6064 {
6065 attr.GetTextBoxAttr().Reset();
6066 // The background colour will be painted by the container, and we don't
6067 // want to unnecessarily overwrite the background when we're drawing text
6068 // because this may erase the guideline (which appears just under the text
6069 // if there's no padding).
6070 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6071 }
fe5aa22c
JS
6072 wxRichTextApplyStyle(attr, GetAttributes());
6073 }
6074 else
6075 attr = GetAttributes();
6076
6077 wxRichTextApplyStyle(attr, contentStyle);
6078 return attr;
6079}
6080
6081/// Get combined attributes of the base style and paragraph style.
603f702b 6082wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
fe5aa22c 6083{
24777478 6084 wxRichTextAttr attr;
603f702b 6085 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
fe5aa22c
JS
6086 if (buf)
6087 {
6088 attr = buf->GetBasicStyle();
603f702b
JS
6089 if (!includingBoxAttr)
6090 attr.GetTextBoxAttr().Reset();
fe5aa22c
JS
6091 wxRichTextApplyStyle(attr, GetAttributes());
6092 }
6093 else
6094 attr = GetAttributes();
6095
6096 return attr;
6097}
5d7836c4 6098
603f702b 6099// Create default tabstop array
cfa3b256
JS
6100void wxRichTextParagraph::InitDefaultTabs()
6101{
6102 // create a default tab list at 10 mm each.
6103 for (int i = 0; i < 20; ++i)
6104 {
6105 sm_defaultTabs.Add(i*100);
6106 }
6107}
6108
603f702b 6109// Clear default tabstop array
cfa3b256
JS
6110void wxRichTextParagraph::ClearDefaultTabs()
6111{
6112 sm_defaultTabs.Clear();
6113}
6114
8db2e3ef 6115void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
cdaed652
VZ
6116{
6117 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6118 while (node)
6119 {
bec80f4f 6120 wxRichTextObject* anchored = node->GetData();
07d4142f 6121 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
cdaed652
VZ
6122 {
6123 wxSize size;
6124 int descent, x = 0;
8db2e3ef 6125 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
bec80f4f 6126
24777478 6127 int offsetY = 0;
603f702b 6128 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
24777478
JS
6129 {
6130 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6131 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6132 {
6133 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6134 }
6135 }
bec80f4f 6136
24777478 6137 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
ce00f59b 6138
cdaed652 6139 /* Update the offset */
24777478
JS
6140 int newOffsetY = pos - rect.y;
6141 if (newOffsetY != offsetY)
6142 {
6143 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6144 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6145 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6146 }
cdaed652 6147
24777478 6148 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
603f702b 6149 x = rect.x;
24777478 6150 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
603f702b 6151 x = rect.x + rect.width - size.x;
24777478 6152
cdaed652
VZ
6153 anchored->SetPosition(wxPoint(x, pos));
6154 anchored->SetCachedSize(size);
6155 floatCollector->CollectFloat(this, anchored);
6156 }
6157
6158 node = node->GetNext();
6159 }
6160}
6161
603f702b 6162// Get the first position from pos that has a line break character.
ff76711f
JS
6163long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6164{
6165 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6166 while (node)
6167 {
6168 wxRichTextObject* obj = node->GetData();
6169 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6170 {
6171 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6172 if (textObj)
6173 {
6174 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6175 if (breakPos > -1)
6176 return breakPos;
6177 }
6178 }
6179 node = node->GetNext();
6180 }
6181 return -1;
6182}
cfa3b256 6183
5d7836c4
JS
6184/*!
6185 * wxRichTextLine
6186 * This object represents a line in a paragraph, and stores
6187 * offsets from the start of the paragraph representing the
6188 * start and end positions of the line.
6189 */
6190
6191wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6192{
1e967276 6193 Init(parent);
5d7836c4
JS
6194}
6195
6196/// Initialisation
1e967276 6197void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 6198{
1e967276
JS
6199 m_parent = parent;
6200 m_range.SetRange(-1, -1);
6201 m_pos = wxPoint(0, 0);
6202 m_size = wxSize(0, 0);
5d7836c4 6203 m_descent = 0;
2f45f554
JS
6204#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6205 m_objectSizes.Clear();
6206#endif
5d7836c4
JS
6207}
6208
6209/// Copy
6210void wxRichTextLine::Copy(const wxRichTextLine& obj)
6211{
6212 m_range = obj.m_range;
2f45f554
JS
6213#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6214 m_objectSizes = obj.m_objectSizes;
6215#endif
5d7836c4
JS
6216}
6217
6218/// Get the absolute object position
6219wxPoint wxRichTextLine::GetAbsolutePosition() const
6220{
6221 return m_parent->GetPosition() + m_pos;
6222}
6223
1e967276
JS
6224/// Get the absolute range
6225wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6226{
6227 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6228 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6229 return range;
6230}
6231
5d7836c4
JS
6232/*!
6233 * wxRichTextPlainText
6234 * This object represents a single piece of text.
6235 */
6236
6237IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6238
24777478 6239wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5d7836c4
JS
6240 wxRichTextObject(parent)
6241{
5d7836c4
JS
6242 if (style)
6243 SetAttributes(*style);
6244
6245 m_text = text;
6246}
6247
cfa3b256
JS
6248#define USE_KERNING_FIX 1
6249
4794d69c
JS
6250// If insufficient tabs are defined, this is the tab width used
6251#define WIDTH_FOR_DEFAULT_TABS 50
6252
5d7836c4 6253/// Draw the item
8db2e3ef 6254bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5d7836c4 6255{
fe5aa22c
JS
6256 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6257 wxASSERT (para != NULL);
6258
603f702b 6259 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
8db2e3ef 6260 context.ApplyVirtualAttributes(textAttr, this);
603f702b
JS
6261
6262 // Let's make the assumption for now that for content in a paragraph, including
6263 // text, we never have a discontinuous selection. So we only deal with a
6264 // single range.
6265 wxRichTextRange selectionRange;
6266 if (selection.IsValid())
6267 {
6268 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6269 if (selectionRanges.GetCount() > 0)
6270 selectionRange = selectionRanges[0];
6271 else
6272 selectionRange = wxRICHTEXT_NO_SELECTION;
6273 }
6274 else
6275 selectionRange = wxRICHTEXT_NO_SELECTION;
fe5aa22c 6276
5d7836c4
JS
6277 int offset = GetRange().GetStart();
6278
ff76711f
JS
6279 // Replace line break characters with spaces
6280 wxString str = m_text;
6281 wxString toRemove = wxRichTextLineBreakChar;
6282 str.Replace(toRemove, wxT(" "));
d07f2e19 6283 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
c025e094 6284 str.MakeUpper();
3e541562 6285
5d7836c4 6286 long len = range.GetLength();
ff76711f 6287 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5d7836c4 6288
5d7836c4
JS
6289 // Test for the optimized situations where all is selected, or none
6290 // is selected.
6291
30bf7630
JS
6292 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6293 wxCheckSetFont(dc, textFont);
6294 int charHeight = dc.GetCharHeight();
6295
6296 int x, y;
a1b806b9 6297 if ( textFont.IsOk() )
30bf7630 6298 {
d07f2e19
JS
6299 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6300 {
6301 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6302 wxCheckSetFont(dc, textFont);
6303 charHeight = dc.GetCharHeight();
6304 }
6305
30bf7630
JS
6306 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6307 {
32423dd8
JS
6308 if (textFont.IsUsingSizeInPixels())
6309 {
6310 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
4ba36292 6311 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
32423dd8
JS
6312 x = rect.x;
6313 y = rect.y;
6314 }
6315 else
6316 {
6317 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6318 textFont.SetPointSize(static_cast<int>(size));
32423dd8
JS
6319 x = rect.x;
6320 y = rect.y;
6321 }
30bf7630
JS
6322 wxCheckSetFont(dc, textFont);
6323 }
6324 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6325 {
32423dd8
JS
6326 if (textFont.IsUsingSizeInPixels())
6327 {
6328 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6329 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6330 x = rect.x;
4ba36292 6331 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6332 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6333 }
6334 else
6335 {
6336 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
4ba36292 6337 textFont.SetPointSize(static_cast<int>(size));
32423dd8 6338 x = rect.x;
4ba36292 6339 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
32423dd8
JS
6340 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6341 }
30bf7630
JS
6342 wxCheckSetFont(dc, textFont);
6343 }
6344 else
6345 {
6346 x = rect.x;
6347 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6348 }
6349 }
6350 else
6351 {
6352 x = rect.x;
6353 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6354 }
5d7836c4 6355
603f702b
JS
6356 // TODO: new selection code
6357
5d7836c4
JS
6358 // (a) All selected.
6359 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 6360 {
fe5aa22c 6361 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
6362 }
6363 // (b) None selected.
6364 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6365 {
6366 // Draw all unselected
fe5aa22c 6367 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
6368 }
6369 else
6370 {
6371 // (c) Part selected, part not
6372 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6373
04ee05f9 6374 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7fe8059f 6375
5d7836c4
JS
6376 // 1. Initial unselected chunk, if any, up until start of selection.
6377 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6378 {
6379 int r1 = range.GetStart();
6380 int s1 = selectionRange.GetStart()-1;
6381 int fragmentLen = s1 - r1 + 1;
6382 if (fragmentLen < 0)
af588446 6383 {
5d7836c4 6384 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
af588446 6385 }
ff76711f 6386 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
5d7836c4 6387
fe5aa22c 6388 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
6389
6390#if USE_KERNING_FIX
6391 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6392 {
6393 // Compensate for kerning difference
ff76711f
JS
6394 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6395 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
41a85215 6396
cfa3b256
JS
6397 wxCoord w1, h1, w2, h2, w3, h3;
6398 dc.GetTextExtent(stringFragment, & w1, & h1);
6399 dc.GetTextExtent(stringFragment2, & w2, & h2);
6400 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6401
cfa3b256
JS
6402 int kerningDiff = (w1 + w3) - w2;
6403 x = x - kerningDiff;
6404 }
6405#endif
5d7836c4
JS
6406 }
6407
6408 // 2. Selected chunk, if any.
6409 if (selectionRange.GetEnd() >= range.GetStart())
6410 {
6411 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6412 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6413
6414 int fragmentLen = s2 - s1 + 1;
6415 if (fragmentLen < 0)
af588446 6416 {
5d7836c4 6417 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
af588446 6418 }
ff76711f 6419 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
5d7836c4 6420
fe5aa22c 6421 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
6422
6423#if USE_KERNING_FIX
6424 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6425 {
6426 // Compensate for kerning difference
ff76711f
JS
6427 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6428 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
41a85215 6429
cfa3b256
JS
6430 wxCoord w1, h1, w2, h2, w3, h3;
6431 dc.GetTextExtent(stringFragment, & w1, & h1);
6432 dc.GetTextExtent(stringFragment2, & w2, & h2);
6433 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 6434
cfa3b256
JS
6435 int kerningDiff = (w1 + w3) - w2;
6436 x = x - kerningDiff;
6437 }
6438#endif
5d7836c4
JS
6439 }
6440
6441 // 3. Remaining unselected chunk, if any
6442 if (selectionRange.GetEnd() < range.GetEnd())
6443 {
6444 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6445 int r2 = range.GetEnd();
6446
6447 int fragmentLen = r2 - s2 + 1;
6448 if (fragmentLen < 0)
af588446 6449 {
5d7836c4 6450 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
af588446 6451 }
ff76711f 6452 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
ab14c7aa 6453
fe5aa22c 6454 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 6455 }
5d7836c4
JS
6456 }
6457
6458 return true;
6459}
61399247 6460
24777478 6461bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 6462{
cfa3b256
JS
6463 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6464
6465 wxArrayInt tabArray;
6466 int tabCount;
6467 if (hasTabs)
ab14c7aa 6468 {
cfa3b256
JS
6469 if (attr.GetTabs().IsEmpty())
6470 tabArray = wxRichTextParagraph::GetDefaultTabs();
6471 else
6472 tabArray = attr.GetTabs();
6473 tabCount = tabArray.GetCount();
6474
6475 for (int i = 0; i < tabCount; ++i)
ab14c7aa 6476 {
cfa3b256
JS
6477 int pos = tabArray[i];
6478 pos = ConvertTenthsMMToPixels(dc, pos);
6479 tabArray[i] = pos;
7f0d9d71
JS
6480 }
6481 }
cfa3b256
JS
6482 else
6483 tabCount = 0;
ab14c7aa 6484
cfa3b256
JS
6485 int nextTabPos = -1;
6486 int tabPos = -1;
7f0d9d71 6487 wxCoord w, h;
ab14c7aa 6488
cfa3b256 6489 if (selected)
ab14c7aa 6490 {
0ec6da02
JS
6491 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6492 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6493
ecb5fbf1
JS
6494 wxCheckSetBrush(dc, wxBrush(highlightColour));
6495 wxCheckSetPen(dc, wxPen(highlightColour));
0ec6da02 6496 dc.SetTextForeground(highlightTextColour);
04ee05f9 6497 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7f0d9d71 6498 }
ab14c7aa
JS
6499 else
6500 {
fe5aa22c 6501 dc.SetTextForeground(attr.GetTextColour());
ab14c7aa 6502
f0e9eda2
JS
6503 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6504 {
04ee05f9 6505 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
f0e9eda2
JS
6506 dc.SetTextBackground(attr.GetBackgroundColour());
6507 }
6508 else
04ee05f9 6509 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
3e541562 6510 }
3e541562 6511
925a662a 6512 wxCoord x_orig = GetParent()->GetPosition().x;
cfa3b256 6513 while (hasTabs)
ab14c7aa
JS
6514 {
6515 // the string has a tab
7f0d9d71
JS
6516 // break up the string at the Tab
6517 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6518 str = str.AfterFirst(wxT('\t'));
6519 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 6520 tabPos = x + w;
7f0d9d71 6521 bool not_found = true;
cfa3b256 6522 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 6523 {
015d0446 6524 nextTabPos = tabArray.Item(i) + x_orig;
4794d69c
JS
6525
6526 // Find the next tab position.
6527 // Even if we're at the end of the tab array, we must still draw the chunk.
6528
6529 if (nextTabPos > tabPos || (i == (tabCount - 1)))
ab14c7aa 6530 {
4794d69c
JS
6531 if (nextTabPos <= tabPos)
6532 {
6533 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6534 nextTabPos = tabPos + defaultTabWidth;
6535 }
6536
7f0d9d71 6537 not_found = false;
ab14c7aa
JS
6538 if (selected)
6539 {
cfa3b256 6540 w = nextTabPos - x;
7f0d9d71 6541 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 6542 dc.DrawRectangle(selRect);
7f0d9d71
JS
6543 }
6544 dc.DrawText(stringChunk, x, y);
42688aea
JS
6545
6546 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6547 {
6548 wxPen oldPen = dc.GetPen();
ecb5fbf1 6549 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6550 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6551 wxCheckSetPen(dc, oldPen);
42688aea
JS
6552 }
6553
cfa3b256 6554 x = nextTabPos;
7f0d9d71
JS
6555 }
6556 }
cfa3b256 6557 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 6558 }
61399247 6559
cfa3b256 6560 if (!str.IsEmpty())
ab14c7aa 6561 {
cfa3b256
JS
6562 dc.GetTextExtent(str, & w, & h);
6563 if (selected)
6564 {
6565 wxRect selRect(x, rect.y, w, rect.GetHeight());
6566 dc.DrawRectangle(selRect);
6567 }
6568 dc.DrawText(str, x, y);
42688aea
JS
6569
6570 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6571 {
6572 wxPen oldPen = dc.GetPen();
ecb5fbf1 6573 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
42688aea 6574 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
ecb5fbf1 6575 wxCheckSetPen(dc, oldPen);
42688aea
JS
6576 }
6577
cfa3b256 6578 x += w;
7f0d9d71 6579 }
5d7836c4 6580
7c9fdebe 6581 return true;
7f0d9d71 6582}
fe5aa22c 6583
5d7836c4 6584/// Lay the item out
8db2e3ef 6585bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 6586{
ecb5fbf1
JS
6587 // Only lay out if we haven't already cached the size
6588 if (m_size.x == -1)
8db2e3ef 6589 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
603f702b
JS
6590 m_maxSize = m_size;
6591 // Eventually we want to have a reasonable estimate of minimum size.
6592 m_minSize = wxSize(0, 0);
5d7836c4
JS
6593 return true;
6594}
6595
6596/// Copy
6597void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6598{
6599 wxRichTextObject::Copy(obj);
6600
6601 m_text = obj.m_text;
6602}
6603
6604/// Get/set the object size for the given range. Returns false if the range
6605/// is invalid for this object.
8db2e3ef 6606bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
5d7836c4
JS
6607{
6608 if (!range.IsWithin(GetRange()))
6609 return false;
6610
fe5aa22c
JS
6611 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6612 wxASSERT (para != NULL);
603f702b 6613
925a662a 6614 int relativeX = position.x - GetParent()->GetPosition().x;
fe5aa22c 6615
24777478 6616 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
8db2e3ef 6617 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
fe5aa22c 6618
5d7836c4
JS
6619 // Always assume unformatted text, since at this level we have no knowledge
6620 // of line breaks - and we don't need it, since we'll calculate size within
6621 // formatted text by doing it in chunks according to the line ranges
6622
30bf7630 6623 bool bScript(false);
44cc96a8 6624 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
a1b806b9 6625 if (font.IsOk())
30bf7630
JS
6626 {
6627 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6628 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6629 {
6630 wxFont textFont = font;
32423dd8
JS
6631 if (textFont.IsUsingSizeInPixels())
6632 {
6633 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6634 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6635 }
6636 else
6637 {
6638 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6639 textFont.SetPointSize(static_cast<int>(size));
6640 }
30bf7630
JS
6641 wxCheckSetFont(dc, textFont);
6642 bScript = true;
6643 }
d07f2e19
JS
6644 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6645 {
6646 wxFont textFont = font;
6647 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6648 wxCheckSetFont(dc, textFont);
6649 bScript = true;
6650 }
30bf7630
JS
6651 else
6652 {
6653 wxCheckSetFont(dc, font);
6654 }
6655 }
5d7836c4 6656
109bfc88 6657 bool haveDescent = false;
5d7836c4
JS
6658 int startPos = range.GetStart() - GetRange().GetStart();
6659 long len = range.GetLength();
3e541562 6660
ff76711f
JS
6661 wxString str(m_text);
6662 wxString toReplace = wxRichTextLineBreakChar;
6663 str.Replace(toReplace, wxT(" "));
6664
6665 wxString stringChunk = str.Mid(startPos, (size_t) len);
42688aea 6666
d07f2e19 6667 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
42688aea
JS
6668 stringChunk.MakeUpper();
6669
5d7836c4 6670 wxCoord w, h;
7f0d9d71 6671 int width = 0;
cfa3b256 6672 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
6673 {
6674 // the string has a tab
cfa3b256
JS
6675 wxArrayInt tabArray;
6676 if (textAttr.GetTabs().IsEmpty())
6677 tabArray = wxRichTextParagraph::GetDefaultTabs();
6678 else
6679 tabArray = textAttr.GetTabs();
ab14c7aa 6680
cfa3b256 6681 int tabCount = tabArray.GetCount();
41a85215 6682
cfa3b256 6683 for (int i = 0; i < tabCount; ++i)
61399247 6684 {
cfa3b256
JS
6685 int pos = tabArray[i];
6686 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6687 tabArray[i] = pos;
7f0d9d71 6688 }
41a85215 6689
cfa3b256 6690 int nextTabPos = -1;
61399247 6691
ab14c7aa
JS
6692 while (stringChunk.Find(wxT('\t')) >= 0)
6693 {
109bfc88
JS
6694 int absoluteWidth = 0;
6695
ab14c7aa 6696 // the string has a tab
7f0d9d71
JS
6697 // break up the string at the Tab
6698 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6699 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4794d69c 6700
31778480
JS
6701 if (partialExtents)
6702 {
109bfc88
JS
6703 int oldWidth;
6704 if (partialExtents->GetCount() > 0)
6705 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6706 else
6707 oldWidth = 0;
6708
31778480
JS
6709 // Add these partial extents
6710 wxArrayInt p;
6711 dc.GetPartialTextExtents(stringFragment, p);
6712 size_t j;
6713 for (j = 0; j < p.GetCount(); j++)
6714 partialExtents->Add(oldWidth + p[j]);
109bfc88
JS
6715
6716 if (partialExtents->GetCount() > 0)
925a662a 6717 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
109bfc88 6718 else
925a662a 6719 absoluteWidth = relativeX;
109bfc88
JS
6720 }
6721 else
6722 {
6723 dc.GetTextExtent(stringFragment, & w, & h);
6724 width += w;
603f702b 6725 absoluteWidth = width + relativeX;
109bfc88 6726 haveDescent = true;
31778480
JS
6727 }
6728
cfa3b256
JS
6729 bool notFound = true;
6730 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 6731 {
cfa3b256 6732 nextTabPos = tabArray.Item(i);
4794d69c
JS
6733
6734 // Find the next tab position.
6735 // Even if we're at the end of the tab array, we must still process the chunk.
6736
6737 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
ab14c7aa 6738 {
4794d69c
JS
6739 if (nextTabPos <= absoluteWidth)
6740 {
6741 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6742 nextTabPos = absoluteWidth + defaultTabWidth;
6743 }
6744
cfa3b256 6745 notFound = false;
925a662a 6746 width = nextTabPos - relativeX;
31778480
JS
6747
6748 if (partialExtents)
6749 partialExtents->Add(width);
7f0d9d71
JS
6750 }
6751 }
6752 }
6753 }
30bf7630 6754
31778480
JS
6755 if (!stringChunk.IsEmpty())
6756 {
31778480
JS
6757 if (partialExtents)
6758 {
109bfc88
JS
6759 int oldWidth;
6760 if (partialExtents->GetCount() > 0)
6761 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6762 else
6763 oldWidth = 0;
6764
31778480
JS
6765 // Add these partial extents
6766 wxArrayInt p;
6767 dc.GetPartialTextExtents(stringChunk, p);
6768 size_t j;
6769 for (j = 0; j < p.GetCount(); j++)
6770 partialExtents->Add(oldWidth + p[j]);
6771 }
109bfc88
JS
6772 else
6773 {
6774 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6775 width += w;
6776 haveDescent = true;
6777 }
6778 }
6779
6780 if (partialExtents)
6781 {
6782 int charHeight = dc.GetCharHeight();
6783 if ((*partialExtents).GetCount() > 0)
6784 w = (*partialExtents)[partialExtents->GetCount()-1];
6785 else
6786 w = 0;
6787 size = wxSize(w, charHeight);
6788 }
6789 else
6790 {
6791 size = wxSize(width, dc.GetCharHeight());
31778480 6792 }
30bf7630 6793
109bfc88
JS
6794 if (!haveDescent)
6795 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6796
30bf7630
JS
6797 if ( bScript )
6798 dc.SetFont(font);
6799
5d7836c4
JS
6800 return true;
6801}
6802
6803/// Do a split, returning an object containing the second part, and setting
6804/// the first part in 'this'.
6805wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6806{
ff76711f 6807 long index = pos - GetRange().GetStart();
3e541562 6808
28f92d74 6809 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
6810 return NULL;
6811
6812 wxString firstPart = m_text.Mid(0, index);
6813 wxString secondPart = m_text.Mid(index);
6814
6815 m_text = firstPart;
6816
6817 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6818 newObject->SetAttributes(GetAttributes());
8db2e3ef 6819 newObject->SetProperties(GetProperties());
5d7836c4
JS
6820
6821 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6822 GetRange().SetEnd(pos-1);
3e541562 6823
5d7836c4
JS
6824 return newObject;
6825}
6826
6827/// Calculate range
6828void wxRichTextPlainText::CalculateRange(long start, long& end)
6829{
28f92d74 6830 end = start + m_text.length() - 1;
5d7836c4
JS
6831 m_range.SetRange(start, end);
6832}
6833
6834/// Delete range
6835bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6836{
6837 wxRichTextRange r = range;
6838
6839 r.LimitTo(GetRange());
6840
6841 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6842 {
6843 m_text.Empty();
6844 return true;
6845 }
6846
6847 long startIndex = r.GetStart() - GetRange().GetStart();
6848 long len = r.GetLength();
6849
6850 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6851 return true;
6852}
6853
6854/// Get text for the given range.
6855wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6856{
6857 wxRichTextRange r = range;
6858
6859 r.LimitTo(GetRange());
6860
6861 long startIndex = r.GetStart() - GetRange().GetStart();
6862 long len = r.GetLength();
6863
6864 return m_text.Mid(startIndex, len);
6865}
6866
6867/// Returns true if this object can merge itself with the given one.
6868bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
6869{
80a46597 6870 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
8db2e3ef 6871 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
5d7836c4
JS
6872}
6873
6874/// Returns true if this object merged itself with the given one.
6875/// The calling code will then delete the given object.
6876bool wxRichTextPlainText::Merge(wxRichTextObject* object)
6877{
6878 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
6879 wxASSERT( textObject != NULL );
6880
6881 if (textObject)
6882 {
6883 m_text += textObject->GetText();
99404ab0 6884 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
5d7836c4
JS
6885 return true;
6886 }
6887 else
6888 return false;
6889}
6890
6891/// Dump to output stream for debugging
6892void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
6893{
6894 wxRichTextObject::Dump(stream);
6895 stream << m_text << wxT("\n");
6896}
6897
ff76711f
JS
6898/// Get the first position from pos that has a line break character.
6899long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
6900{
6901 int i;
6902 int len = m_text.length();
6903 int startPos = pos - m_range.GetStart();
6904 for (i = startPos; i < len; i++)
6905 {
6906 wxChar ch = m_text[i];
6907 if (ch == wxRichTextLineBreakChar)
6908 {
6909 return i + m_range.GetStart();
6910 }
6911 }
6912 return -1;
6913}
6914
5d7836c4
JS
6915/*!
6916 * wxRichTextBuffer
6917 * This is a kind of box, used to represent the whole buffer
6918 */
6919
6920IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
6921
7c9fdebe
JS
6922wxList wxRichTextBuffer::sm_handlers;
6923wxList wxRichTextBuffer::sm_drawingHandlers;
6924wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
6925wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
6926int wxRichTextBuffer::sm_bulletRightMargin = 20;
6927float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
5d7836c4
JS
6928
6929/// Initialisation
6930void wxRichTextBuffer::Init()
6931{
6932 m_commandProcessor = new wxCommandProcessor;
6933 m_styleSheet = NULL;
6934 m_modified = false;
6935 m_batchedCommandDepth = 0;
6936 m_batchedCommand = NULL;
6937 m_suppressUndo = 0;
d2d0adc7 6938 m_handlerFlags = 0;
44219ff0 6939 m_scale = 1.0;
32423dd8
JS
6940 m_dimensionScale = 1.0;
6941 m_fontScale = 1.0;
f819ed5d 6942 SetMargins(4);
5d7836c4
JS
6943}
6944
6945/// Initialisation
6946wxRichTextBuffer::~wxRichTextBuffer()
6947{
6948 delete m_commandProcessor;
6949 delete m_batchedCommand;
6950
6951 ClearStyleStack();
d2d0adc7 6952 ClearEventHandlers();
5d7836c4
JS
6953}
6954
85d8909b 6955void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 6956{
85d8909b 6957 Reset();
3e541562 6958
5d7836c4 6959 GetCommandProcessor()->ClearCommands();
5d7836c4 6960
5d7836c4 6961 Modify(false);
1e967276 6962 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
6963}
6964
0ca07313
JS
6965void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
6966{
6967 wxRichTextParagraphLayoutBox::Copy(obj);
6968
6969 m_styleSheet = obj.m_styleSheet;
6970 m_modified = obj.m_modified;
bec80f4f
JS
6971 m_batchedCommandDepth = 0;
6972 if (m_batchedCommand)
6973 delete m_batchedCommand;
6974 m_batchedCommand = NULL;
0ca07313 6975 m_suppressUndo = obj.m_suppressUndo;
603f702b 6976 m_invalidRange = obj.m_invalidRange;
32423dd8
JS
6977 m_dimensionScale = obj.m_dimensionScale;
6978 m_fontScale = obj.m_fontScale;
0ca07313
JS
6979}
6980
38f833b1
JS
6981/// Push style sheet to top of stack
6982bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
6983{
6984 if (m_styleSheet)
6985 styleSheet->InsertSheet(m_styleSheet);
6986
6987 SetStyleSheet(styleSheet);
41a85215 6988
38f833b1
JS
6989 return true;
6990}
6991
6992/// Pop style sheet from top of stack
6993wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
6994{
6995 if (m_styleSheet)
6996 {
6997 wxRichTextStyleSheet* oldSheet = m_styleSheet;
6998 m_styleSheet = oldSheet->GetNextSheet();
6999 oldSheet->Unlink();
41a85215 7000
38f833b1
JS
7001 return oldSheet;
7002 }
7003 else
7004 return NULL;
7005}
7006
0ca07313
JS
7007/// Submit command to insert paragraphs
7008bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7009{
4e63bfb9 7010 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
603f702b
JS
7011}
7012
7013/// Submit command to insert paragraphs
4e63bfb9 7014bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
603f702b
JS
7015{
7016 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
0ca07313 7017
0ca07313 7018 action->GetNewParagraphs() = paragraphs;
59509217 7019
0ca07313
JS
7020 action->SetPosition(pos);
7021
603f702b 7022 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
99404ab0
JS
7023 if (!paragraphs.GetPartialParagraph())
7024 range.SetEnd(range.GetEnd()+1);
7025
0ca07313 7026 // Set the range we'll need to delete in Undo
99404ab0 7027 action->SetRange(range);
0ca07313 7028
603f702b 7029 buffer->SubmitAction(action);
0ca07313
JS
7030
7031 return true;
7032}
7033
5d7836c4 7034/// Submit command to insert the given text
fe5aa22c 7035bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7036{
4e63bfb9 7037 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
603f702b
JS
7038}
7039
7040/// Submit command to insert the given text
4e63bfb9 7041bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7042{
7043 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7044
24777478
JS
7045 wxRichTextAttr* p = NULL;
7046 wxRichTextAttr paraAttr;
fe5aa22c
JS
7047 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7048 {
7c081bd2 7049 // Get appropriate paragraph style
603f702b 7050 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
fe5aa22c
JS
7051 if (!paraAttr.IsDefault())
7052 p = & paraAttr;
7053 }
7054
fe5aa22c 7055 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313 7056
603f702b 7057 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
0ca07313 7058
6636ef8d 7059 if (!text.empty() && text.Last() != wxT('\n'))
0ca07313
JS
7060 {
7061 // Don't count the newline when undoing
7062 length --;
5d7836c4 7063 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 7064 }
6636ef8d 7065 else if (!text.empty() && text.Last() == wxT('\n'))
46ee0e5b 7066 length --;
5d7836c4
JS
7067
7068 action->SetPosition(pos);
7069
7070 // Set the range we'll need to delete in Undo
0ca07313 7071 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 7072
603f702b 7073 buffer->SubmitAction(action);
7fe8059f 7074
5d7836c4
JS
7075 return true;
7076}
7077
7078/// Submit command to insert the given text
fe5aa22c 7079bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4 7080{
4e63bfb9 7081 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
603f702b
JS
7082}
7083
7084/// Submit command to insert the given text
4e63bfb9 7085bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
603f702b
JS
7086{
7087 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7088
24777478
JS
7089 wxRichTextAttr* p = NULL;
7090 wxRichTextAttr paraAttr;
fe5aa22c
JS
7091 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7092 {
603f702b 7093 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
fe5aa22c
JS
7094 if (!paraAttr.IsDefault())
7095 p = & paraAttr;
7096 }
7097
603f702b 7098 wxRichTextAttr attr(buffer->GetDefaultStyle());
32423dd8
JS
7099 // Don't include box attributes such as margins
7100 attr.GetTextBoxAttr().Reset();
7fe8059f
WS
7101
7102 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
7103 action->GetNewParagraphs().AppendChild(newPara);
7104 action->GetNewParagraphs().UpdateRanges();
7105 action->GetNewParagraphs().SetPartialParagraph(false);
c025e094
JS
7106 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7107 long pos1 = pos;
7108
6c0ea513
JS
7109 if (p)
7110 newPara->SetAttributes(*p);
7111
c025e094
JS
7112 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7113 {
7114 if (para && para->GetRange().GetEnd() == pos)
7115 pos1 ++;
e2d0875a
JS
7116
7117 // Now see if we need to number the paragraph.
6c0ea513 7118 if (newPara->GetAttributes().HasBulletNumber())
e2d0875a
JS
7119 {
7120 wxRichTextAttr numberingAttr;
7121 if (FindNextParagraphNumber(para, numberingAttr))
7122 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7123 }
c025e094
JS
7124 }
7125
5d7836c4
JS
7126 action->SetPosition(pos);
7127
99404ab0 7128 // Use the default character style
603f702b 7129 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
c025e094
JS
7130 {
7131 // Check whether the default style merely reflects the paragraph/basic style,
7132 // in which case don't apply it.
603f702b 7133 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
32423dd8 7134 defaultStyle.GetTextBoxAttr().Reset();
24777478 7135 wxRichTextAttr toApply;
c025e094
JS
7136 if (para)
7137 {
603f702b 7138 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
24777478 7139 wxRichTextAttr newAttr;
c025e094
JS
7140 // This filters out attributes that are accounted for by the current
7141 // paragraph/basic style
7142 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7143 }
7144 else
7145 toApply = defaultStyle;
7146
7147 if (!toApply.IsDefault())
7148 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7149 }
99404ab0 7150
5d7836c4 7151 // Set the range we'll need to delete in Undo
c025e094 7152 action->SetRange(wxRichTextRange(pos1, pos1));
7fe8059f 7153
603f702b 7154 buffer->SubmitAction(action);
7fe8059f 7155
5d7836c4
JS
7156 return true;
7157}
7158
7159/// Submit command to insert the given image
24777478
JS
7160bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7161 const wxRichTextAttr& textAttr)
5d7836c4 7162{
4e63bfb9 7163 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
603f702b
JS
7164}
7165
7166/// Submit command to insert the given image
4e63bfb9
JS
7167bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7168 wxRichTextCtrl* ctrl, int flags,
603f702b
JS
7169 const wxRichTextAttr& textAttr)
7170{
7171 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
5d7836c4 7172
24777478
JS
7173 wxRichTextAttr* p = NULL;
7174 wxRichTextAttr paraAttr;
fe5aa22c
JS
7175 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7176 {
603f702b 7177 paraAttr = GetStyleForNewParagraph(buffer, pos);
fe5aa22c
JS
7178 if (!paraAttr.IsDefault())
7179 p = & paraAttr;
7180 }
7181
603f702b 7182 wxRichTextAttr attr(buffer->GetDefaultStyle());
7fe8059f 7183
32423dd8
JS
7184 // Don't include box attributes such as margins
7185 attr.GetTextBoxAttr().Reset();
7186
5d7836c4 7187 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
7188 if (p)
7189 newPara->SetAttributes(*p);
7190
5d7836c4
JS
7191 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7192 newPara->AppendChild(imageObject);
24777478 7193 imageObject->SetAttributes(textAttr);
5d7836c4
JS
7194 action->GetNewParagraphs().AppendChild(newPara);
7195 action->GetNewParagraphs().UpdateRanges();
7196
7197 action->GetNewParagraphs().SetPartialParagraph(true);
7198
7199 action->SetPosition(pos);
7200
7201 // Set the range we'll need to delete in Undo
7202 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 7203
603f702b 7204 buffer->SubmitAction(action);
7fe8059f 7205
5d7836c4
JS
7206 return true;
7207}
7208
cdaed652 7209// Insert an object with no change of it
603f702b
JS
7210wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7211{
4e63bfb9 7212 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
603f702b
JS
7213}
7214
7215// Insert an object with no change of it
4e63bfb9 7216wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
cdaed652 7217{
603f702b 7218 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
cdaed652 7219
24777478
JS
7220 wxRichTextAttr* p = NULL;
7221 wxRichTextAttr paraAttr;
cdaed652
VZ
7222 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7223 {
603f702b 7224 paraAttr = GetStyleForNewParagraph(buffer, pos);
cdaed652
VZ
7225 if (!paraAttr.IsDefault())
7226 p = & paraAttr;
7227 }
7228
603f702b 7229 wxRichTextAttr attr(buffer->GetDefaultStyle());
cdaed652 7230
32423dd8
JS
7231 // Don't include box attributes such as margins
7232 attr.GetTextBoxAttr().Reset();
7233
cdaed652
VZ
7234 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7235 if (p)
7236 newPara->SetAttributes(*p);
7237
7238 newPara->AppendChild(object);
7239 action->GetNewParagraphs().AppendChild(newPara);
7240 action->GetNewParagraphs().UpdateRanges();
7241
7242 action->GetNewParagraphs().SetPartialParagraph(true);
7243
7244 action->SetPosition(pos);
7245
7246 // Set the range we'll need to delete in Undo
7247 action->SetRange(wxRichTextRange(pos, pos));
7248
603f702b 7249 buffer->SubmitAction(action);
cdaed652 7250
603f702b
JS
7251 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7252 return obj;
cdaed652 7253}
603f702b 7254
7c9fdebe
JS
7255wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7256 const wxRichTextProperties& properties,
7257 wxRichTextCtrl* ctrl, int flags,
7258 const wxRichTextAttr& textAttr)
7259{
7260 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7261
7262 wxRichTextAttr* p = NULL;
7263 wxRichTextAttr paraAttr;
7264 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7265 {
7266 paraAttr = GetStyleForNewParagraph(buffer, pos);
7267 if (!paraAttr.IsDefault())
7268 p = & paraAttr;
7269 }
7270
7271 wxRichTextAttr attr(buffer->GetDefaultStyle());
7272
32423dd8
JS
7273 // Don't include box attributes such as margins
7274 attr.GetTextBoxAttr().Reset();
7275
7c9fdebe
JS
7276 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7277 if (p)
7278 newPara->SetAttributes(*p);
7279
7280 wxRichTextField* fieldObject = new wxRichTextField();
7281 fieldObject->wxRichTextObject::SetProperties(properties);
7282 fieldObject->SetFieldType(fieldType);
7283 fieldObject->SetAttributes(textAttr);
7284 newPara->AppendChild(fieldObject);
7285 action->GetNewParagraphs().AppendChild(newPara);
7286 action->GetNewParagraphs().UpdateRanges();
7287 action->GetNewParagraphs().SetPartialParagraph(true);
7288 action->SetPosition(pos);
7289
7290 // Set the range we'll need to delete in Undo
7291 action->SetRange(wxRichTextRange(pos, pos));
7292
7293 buffer->SubmitAction(action);
7294
7295 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7296 return obj;
7297}
7298
fe5aa22c
JS
7299/// Get the style that is appropriate for a new paragraph at this position.
7300/// If the previous paragraph has a paragraph style name, look up the next-paragraph
7301/// style.
603f702b 7302wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
fe5aa22c
JS
7303{
7304 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7305 if (para)
7306 {
24777478 7307 wxRichTextAttr attr;
d2d0adc7 7308 bool foundAttributes = false;
3e541562 7309
d2d0adc7 7310 // Look for a matching paragraph style
603f702b 7311 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
fe5aa22c 7312 {
603f702b 7313 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 7314 if (paraDef)
fe5aa22c 7315 {
caad0109
JS
7316 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7317 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
d2d0adc7 7318 {
603f702b 7319 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
d2d0adc7
JS
7320 if (nextParaDef)
7321 {
7322 foundAttributes = true;
603f702b 7323 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7
JS
7324 }
7325 }
3e541562 7326
d2d0adc7
JS
7327 // If we didn't find the 'next style', use this style instead.
7328 if (!foundAttributes)
7329 {
7330 foundAttributes = true;
603f702b 7331 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
d2d0adc7 7332 }
fe5aa22c
JS
7333 }
7334 }
e2d0875a
JS
7335
7336 // Also apply list style if present
603f702b 7337 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
e2d0875a 7338 {
603f702b 7339 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
e2d0875a
JS
7340 if (listDef)
7341 {
7342 int thisIndent = para->GetAttributes().GetLeftIndent();
7343 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7344
7345 // Apply the overall list style, and item style for this level
603f702b 7346 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
e2d0875a
JS
7347 wxRichTextApplyStyle(attr, listStyle);
7348 attr.SetOutlineLevel(thisLevel);
7349 if (para->GetAttributes().HasBulletNumber())
7350 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7351 }
34b4899d 7352 }
e2d0875a 7353
d2d0adc7
JS
7354 if (!foundAttributes)
7355 {
7356 attr = para->GetAttributes();
7357 int flags = attr.GetFlags();
fe5aa22c 7358
d2d0adc7
JS
7359 // Eliminate character styles
7360 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
7361 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7362 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
7363 attr.SetFlags(flags);
7364 }
3e541562 7365
fe5aa22c
JS
7366 return attr;
7367 }
7368 else
24777478 7369 return wxRichTextAttr();
fe5aa22c
JS
7370}
7371
5d7836c4 7372/// Submit command to delete this range
12cc29c5 7373bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
5d7836c4 7374{
603f702b
JS
7375 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7376}
7377
7378/// Submit command to delete this range
7379bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7380{
7381 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7fe8059f 7382
12cc29c5 7383 action->SetPosition(ctrl->GetCaretPosition());
5d7836c4
JS
7384
7385 // Set the range to delete
7386 action->SetRange(range);
7fe8059f 7387
5d7836c4
JS
7388 // Copy the fragment that we'll need to restore in Undo
7389 CopyFragment(range, action->GetOldParagraphs());
7390
6c0ea513
JS
7391 // See if we're deleting a paragraph marker, in which case we need to
7392 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7393 if (range.GetStart() == range.GetEnd())
5d7836c4 7394 {
6c0ea513
JS
7395 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7396 if (para && para->GetRange().GetEnd() == range.GetEnd())
5d7836c4 7397 {
6c0ea513
JS
7398 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7399 if (nextPara && nextPara != para)
5d7836c4 7400 {
6c0ea513
JS
7401 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7402 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
5d7836c4
JS
7403 }
7404 }
7405 }
7406
603f702b 7407 buffer->SubmitAction(action);
7fe8059f 7408
5d7836c4
JS
7409 return true;
7410}
7411
7412/// Collapse undo/redo commands
7413bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7414{
7415 if (m_batchedCommandDepth == 0)
7416 {
7417 wxASSERT(m_batchedCommand == NULL);
7418 if (m_batchedCommand)
7419 {
0745f364 7420 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7421 }
7422 m_batchedCommand = new wxRichTextCommand(cmdName);
7423 }
7424
7fe8059f 7425 m_batchedCommandDepth ++;
5d7836c4
JS
7426
7427 return true;
7428}
7429
7430/// Collapse undo/redo commands
7431bool wxRichTextBuffer::EndBatchUndo()
7432{
7433 m_batchedCommandDepth --;
7434
7435 wxASSERT(m_batchedCommandDepth >= 0);
7436 wxASSERT(m_batchedCommand != NULL);
7437
7438 if (m_batchedCommandDepth == 0)
7439 {
0745f364 7440 GetCommandProcessor()->Store(m_batchedCommand);
5d7836c4
JS
7441 m_batchedCommand = NULL;
7442 }
7443
7444 return true;
7445}
7446
7447/// Submit immediately, or delay according to whether collapsing is on
7448bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7449{
cc2aecde
JS
7450 if (action && !action->GetNewParagraphs().IsEmpty())
7451 PrepareContent(action->GetNewParagraphs());
7452
5d7836c4 7453 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
0745f364
JS
7454 {
7455 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7456 cmd->AddAction(action);
7457 cmd->Do();
7458 cmd->GetActions().Clear();
7459 delete cmd;
7460
5d7836c4 7461 m_batchedCommand->AddAction(action);
0745f364 7462 }
5d7836c4
JS
7463 else
7464 {
7465 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7466 cmd->AddAction(action);
7467
7468 // Only store it if we're not suppressing undo.
7469 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7470 }
7471
7472 return true;
7473}
7474
7475/// Begin suppressing undo/redo commands.
7476bool wxRichTextBuffer::BeginSuppressUndo()
7477{
7fe8059f 7478 m_suppressUndo ++;
5d7836c4
JS
7479
7480 return true;
7481}
7482
7483/// End suppressing undo/redo commands.
7484bool wxRichTextBuffer::EndSuppressUndo()
7485{
7fe8059f 7486 m_suppressUndo --;
5d7836c4
JS
7487
7488 return true;
7489}
7490
7491/// Begin using a style
24777478 7492bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
5d7836c4 7493{
24777478 7494 wxRichTextAttr newStyle(GetDefaultStyle());
32423dd8 7495 newStyle.GetTextBoxAttr().Reset();
5d7836c4
JS
7496
7497 // Save the old default style
32423dd8 7498 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
5d7836c4
JS
7499
7500 wxRichTextApplyStyle(newStyle, style);
7501 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7502
7503 SetDefaultStyle(newStyle);
7504
5d7836c4
JS
7505 return true;
7506}
7507
7508/// End the style
7509bool wxRichTextBuffer::EndStyle()
7510{
63886f6d 7511 if (!m_attributeStack.GetFirst())
5d7836c4
JS
7512 {
7513 wxLogDebug(_("Too many EndStyle calls!"));
7514 return false;
7515 }
7516
09f14108 7517 wxList::compatibility_iterator node = m_attributeStack.GetLast();
24777478 7518 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
9e31a660 7519 m_attributeStack.Erase(node);
5d7836c4
JS
7520
7521 SetDefaultStyle(*attr);
7522
7523 delete attr;
7524 return true;
7525}
7526
7527/// End all styles
7528bool wxRichTextBuffer::EndAllStyles()
7529{
7530 while (m_attributeStack.GetCount() != 0)
7531 EndStyle();
7532 return true;
7533}
7534
7535/// Clear the style stack
7536void wxRichTextBuffer::ClearStyleStack()
7537{
09f14108 7538 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
24777478 7539 delete (wxRichTextAttr*) node->GetData();
5d7836c4
JS
7540 m_attributeStack.Clear();
7541}
7542
7543/// Begin using bold
7544bool wxRichTextBuffer::BeginBold()
7545{
24777478 7546 wxRichTextAttr attr;
7d76fbd5 7547 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7fe8059f 7548
5d7836c4
JS
7549 return BeginStyle(attr);
7550}
7551
7552/// Begin using italic
7553bool wxRichTextBuffer::BeginItalic()
7554{
24777478 7555 wxRichTextAttr attr;
7d76fbd5 7556 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7fe8059f 7557
5d7836c4
JS
7558 return BeginStyle(attr);
7559}
7560
7561/// Begin using underline
7562bool wxRichTextBuffer::BeginUnderline()
7563{
24777478 7564 wxRichTextAttr attr;
44cc96a8 7565 attr.SetFontUnderlined(true);
7fe8059f 7566
5d7836c4
JS
7567 return BeginStyle(attr);
7568}
7569
7570/// Begin using point size
7571bool wxRichTextBuffer::BeginFontSize(int pointSize)
7572{
24777478 7573 wxRichTextAttr attr;
44cc96a8 7574 attr.SetFontSize(pointSize);
7fe8059f 7575
5d7836c4
JS
7576 return BeginStyle(attr);
7577}
7578
7579/// Begin using this font
7580bool wxRichTextBuffer::BeginFont(const wxFont& font)
7581{
24777478 7582 wxRichTextAttr attr;
5d7836c4 7583 attr.SetFont(font);
7fe8059f 7584
5d7836c4
JS
7585 return BeginStyle(attr);
7586}
7587
7588/// Begin using this colour
7589bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7590{
24777478 7591 wxRichTextAttr attr;
5d7836c4
JS
7592 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7593 attr.SetTextColour(colour);
7fe8059f 7594
5d7836c4
JS
7595 return BeginStyle(attr);
7596}
7597
7598/// Begin using alignment
7599bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7600{
24777478 7601 wxRichTextAttr attr;
5d7836c4
JS
7602 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7603 attr.SetAlignment(alignment);
7fe8059f 7604
5d7836c4
JS
7605 return BeginStyle(attr);
7606}
7607
7608/// Begin left indent
7609bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7610{
24777478 7611 wxRichTextAttr attr;
5d7836c4
JS
7612 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7613 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7614
5d7836c4
JS
7615 return BeginStyle(attr);
7616}
7617
7618/// Begin right indent
7619bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7620{
24777478 7621 wxRichTextAttr attr;
5d7836c4
JS
7622 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7623 attr.SetRightIndent(rightIndent);
7fe8059f 7624
5d7836c4
JS
7625 return BeginStyle(attr);
7626}
7627
7628/// Begin paragraph spacing
7629bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7630{
7631 long flags = 0;
7632 if (before != 0)
7633 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7634 if (after != 0)
7635 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7636
24777478 7637 wxRichTextAttr attr;
5d7836c4
JS
7638 attr.SetFlags(flags);
7639 attr.SetParagraphSpacingBefore(before);
7640 attr.SetParagraphSpacingAfter(after);
7fe8059f 7641
5d7836c4
JS
7642 return BeginStyle(attr);
7643}
7644
7645/// Begin line spacing
7646bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
7647{
24777478 7648 wxRichTextAttr attr;
5d7836c4
JS
7649 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
7650 attr.SetLineSpacing(lineSpacing);
7fe8059f 7651
5d7836c4
JS
7652 return BeginStyle(attr);
7653}
7654
7655/// Begin numbered bullet
7656bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
7657{
24777478 7658 wxRichTextAttr attr;
f089713f 7659 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7660 attr.SetBulletStyle(bulletStyle);
7661 attr.SetBulletNumber(bulletNumber);
7662 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 7663
5d7836c4
JS
7664 return BeginStyle(attr);
7665}
7666
7667/// Begin symbol bullet
d2d0adc7 7668bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4 7669{
24777478 7670 wxRichTextAttr attr;
f089713f 7671 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
7672 attr.SetBulletStyle(bulletStyle);
7673 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 7674 attr.SetBulletText(symbol);
7fe8059f 7675
5d7836c4
JS
7676 return BeginStyle(attr);
7677}
7678
f089713f
JS
7679/// Begin standard bullet
7680bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
7681{
24777478 7682 wxRichTextAttr attr;
f089713f
JS
7683 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7684 attr.SetBulletStyle(bulletStyle);
7685 attr.SetLeftIndent(leftIndent, leftSubIndent);
7686 attr.SetBulletName(bulletName);
7687
7688 return BeginStyle(attr);
7689}
7690
5d7836c4
JS
7691/// Begin named character style
7692bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
7693{
7694 if (GetStyleSheet())
7695 {
7696 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7697 if (def)
7698 {
24777478 7699 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7700 return BeginStyle(attr);
7701 }
7702 }
7703 return false;
7704}
7705
7706/// Begin named paragraph style
7707bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
7708{
7709 if (GetStyleSheet())
7710 {
7711 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
7712 if (def)
7713 {
24777478 7714 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
5d7836c4
JS
7715 return BeginStyle(attr);
7716 }
7717 }
7718 return false;
7719}
7720
f089713f
JS
7721/// Begin named list style
7722bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
7723{
7724 if (GetStyleSheet())
7725 {
7726 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
7727 if (def)
7728 {
24777478 7729 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
f089713f
JS
7730
7731 attr.SetBulletNumber(number);
7732
7733 return BeginStyle(attr);
7734 }
7735 }
7736 return false;
7737}
7738
d2d0adc7
JS
7739/// Begin URL
7740bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
7741{
24777478 7742 wxRichTextAttr attr;
d2d0adc7
JS
7743
7744 if (!characterStyle.IsEmpty() && GetStyleSheet())
7745 {
7746 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7747 if (def)
7748 {
336d8ae9 7749 attr = def->GetStyleMergedWithBase(GetStyleSheet());
d2d0adc7
JS
7750 }
7751 }
7752 attr.SetURL(url);
7753
7754 return BeginStyle(attr);
7755}
7756
5d7836c4
JS
7757/// Adds a handler to the end
7758void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
7759{
7760 sm_handlers.Append(handler);
7761}
7762
7763/// Inserts a handler at the front
7764void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
7765{
7766 sm_handlers.Insert( handler );
7767}
7768
7769/// Removes a handler
7770bool wxRichTextBuffer::RemoveHandler(const wxString& name)
7771{
7772 wxRichTextFileHandler *handler = FindHandler(name);
7773 if (handler)
7774 {
7775 sm_handlers.DeleteObject(handler);
7776 delete handler;
7777 return true;
7778 }
7779 else
7780 return false;
7781}
7782
7783/// Finds a handler by filename or, if supplied, type
d75a69e8
FM
7784wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
7785 wxRichTextFileType imageType)
5d7836c4
JS
7786{
7787 if (imageType != wxRICHTEXT_TYPE_ANY)
7788 return FindHandler(imageType);
0ca07313 7789 else if (!filename.IsEmpty())
5d7836c4
JS
7790 {
7791 wxString path, file, ext;
a51e601e 7792 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4
JS
7793 return FindHandler(ext, imageType);
7794 }
0ca07313
JS
7795 else
7796 return NULL;
5d7836c4
JS
7797}
7798
7799
7800/// Finds a handler by name
7801wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
7802{
7803 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7804 while (node)
7805 {
7806 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7807 if (handler->GetName().Lower() == name.Lower()) return handler;
7808
7809 node = node->GetNext();
7810 }
7811 return NULL;
7812}
7813
7814/// Finds a handler by extension and type
d75a69e8 7815wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
5d7836c4
JS
7816{
7817 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7818 while (node)
7819 {
7820 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7821 if ( handler->GetExtension().Lower() == extension.Lower() &&
7822 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
7823 return handler;
7824 node = node->GetNext();
7825 }
7826 return 0;
7827}
7828
7829/// Finds a handler by type
d75a69e8 7830wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
5d7836c4
JS
7831{
7832 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7833 while (node)
7834 {
7835 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
7836 if (handler->GetType() == type) return handler;
7837 node = node->GetNext();
7838 }
7839 return NULL;
7840}
7841
7842void wxRichTextBuffer::InitStandardHandlers()
7843{
7844 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
7845 AddHandler(new wxRichTextPlainTextHandler);
7846}
7847
7848void wxRichTextBuffer::CleanUpHandlers()
7849{
7850 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7851 while (node)
7852 {
7853 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
7854 wxList::compatibility_iterator next = node->GetNext();
7855 delete handler;
7856 node = next;
7857 }
7858
7859 sm_handlers.Clear();
7860}
7861
1e967276 7862wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 7863{
1e967276
JS
7864 if (types)
7865 types->Clear();
7866
5d7836c4
JS
7867 wxString wildcard;
7868
7869 wxList::compatibility_iterator node = GetHandlers().GetFirst();
7870 int count = 0;
7871 while (node)
7872 {
7873 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
2a230426 7874 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
5d7836c4
JS
7875 {
7876 if (combine)
7877 {
7878 if (count > 0)
7879 wildcard += wxT(";");
7880 wildcard += wxT("*.") + handler->GetExtension();
7881 }
7882 else
7883 {
7884 if (count > 0)
7885 wildcard += wxT("|");
7886 wildcard += handler->GetName();
7887 wildcard += wxT(" ");
7888 wildcard += _("files");
7889 wildcard += wxT(" (*.");
7890 wildcard += handler->GetExtension();
7891 wildcard += wxT(")|*.");
7892 wildcard += handler->GetExtension();
1e967276
JS
7893 if (types)
7894 types->Add(handler->GetType());
5d7836c4
JS
7895 }
7896 count ++;
7897 }
7898
7899 node = node->GetNext();
7900 }
7901
7902 if (combine)
7903 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
7904 return wildcard;
7905}
7906
7907/// Load a file
d75a69e8 7908bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7909{
7910 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7911 if (handler)
1e967276 7912 {
24777478 7913 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7914 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7915 bool success = handler->LoadFile(this, filename);
7916 Invalidate(wxRICHTEXT_ALL);
7917 return success;
7918 }
5d7836c4
JS
7919 else
7920 return false;
7921}
7922
7923/// Save a file
d75a69e8 7924bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
5d7836c4
JS
7925{
7926 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7927 if (handler)
d2d0adc7
JS
7928 {
7929 handler->SetFlags(GetHandlerFlags());
5d7836c4 7930 return handler->SaveFile(this, filename);
d2d0adc7 7931 }
5d7836c4
JS
7932 else
7933 return false;
7934}
7935
7936/// Load from a stream
d75a69e8 7937bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7938{
7939 wxRichTextFileHandler* handler = FindHandler(type);
7940 if (handler)
1e967276 7941 {
24777478 7942 SetDefaultStyle(wxRichTextAttr());
d2d0adc7 7943 handler->SetFlags(GetHandlerFlags());
1e967276
JS
7944 bool success = handler->LoadFile(this, stream);
7945 Invalidate(wxRICHTEXT_ALL);
7946 return success;
7947 }
5d7836c4
JS
7948 else
7949 return false;
7950}
7951
7952/// Save to a stream
d75a69e8 7953bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
5d7836c4
JS
7954{
7955 wxRichTextFileHandler* handler = FindHandler(type);
7956 if (handler)
d2d0adc7
JS
7957 {
7958 handler->SetFlags(GetHandlerFlags());
5d7836c4 7959 return handler->SaveFile(this, stream);
d2d0adc7 7960 }
5d7836c4
JS
7961 else
7962 return false;
7963}
7964
7965/// Copy the range to the clipboard
7966bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
7967{
7968 bool success = false;
603f702b
JS
7969 wxRichTextParagraphLayoutBox* container = this;
7970 if (GetRichTextCtrl())
7971 container = GetRichTextCtrl()->GetFocusObject();
7972
11ef729d 7973#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 7974
d2142335 7975 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 7976 {
0ca07313
JS
7977 wxTheClipboard->Clear();
7978
7979 // Add composite object
7980
7981 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
7982
7983 {
603f702b 7984 wxString text = container->GetTextForRange(range);
0ca07313
JS
7985
7986#ifdef __WXMSW__
7987 text = wxTextFile::Translate(text, wxTextFileType_Dos);
7988#endif
7989
7990 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
7991 }
7992
7993 // Add rich text buffer data object. This needs the XML handler to be present.
7994
7995 if (FindHandler(wxRICHTEXT_TYPE_XML))
7996 {
7997 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
603f702b 7998 container->CopyFragment(range, *richTextBuf);
0ca07313
JS
7999
8000 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8001 }
8002
8003 if (wxTheClipboard->SetData(compositeObject))
8004 success = true;
8005
5d7836c4
JS
8006 wxTheClipboard->Close();
8007 }
0ca07313 8008
39a1c2f2
WS
8009#else
8010 wxUnusedVar(range);
8011#endif
5d7836c4
JS
8012 return success;
8013}
8014
8015/// Paste the clipboard content to the buffer
8016bool wxRichTextBuffer::PasteFromClipboard(long position)
8017{
8018 bool success = false;
603f702b
JS
8019 wxRichTextParagraphLayoutBox* container = this;
8020 if (GetRichTextCtrl())
8021 container = GetRichTextCtrl()->GetFocusObject();
8022
11ef729d 8023#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
8024 if (CanPasteFromClipboard())
8025 {
8026 if (wxTheClipboard->Open())
8027 {
0ca07313
JS
8028 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8029 {
8030 wxRichTextBufferDataObject data;
8031 wxTheClipboard->GetData(data);
8032 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8033 if (richTextBuffer)
8034 {
4e63bfb9 8035 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
62381daa 8036 if (GetRichTextCtrl())
603f702b 8037 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
0ca07313
JS
8038 delete richTextBuffer;
8039 }
8040 }
f7d83f24 8041 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
603f702b
JS
8042 #if wxUSE_UNICODE
8043 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8044 #endif
f7d83f24 8045 )
5d7836c4
JS
8046 {
8047 wxTextDataObject data;
8048 wxTheClipboard->GetData(data);
8049 wxString text(data.GetText());
c21f3211
JS
8050#ifdef __WXMSW__
8051 wxString text2;
8052 text2.Alloc(text.Length()+1);
8053 size_t i;
8054 for (i = 0; i < text.Length(); i++)
8055 {
8056 wxChar ch = text[i];
8057 if (ch != wxT('\r'))
8058 text2 += ch;
8059 }
8060#else
8061 wxString text2 = text;
8062#endif
4e63bfb9 8063 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7fe8059f 8064
62381daa
JS
8065 if (GetRichTextCtrl())
8066 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8067
5d7836c4
JS
8068 success = true;
8069 }
8070 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8071 {
8072 wxBitmapDataObject data;
8073 wxTheClipboard->GetData(data);
8074 wxBitmap bitmap(data.GetBitmap());
8075 wxImage image(bitmap.ConvertToImage());
8076
603f702b 8077 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7fe8059f 8078
5d7836c4
JS
8079 action->GetNewParagraphs().AddImage(image);
8080
8081 if (action->GetNewParagraphs().GetChildCount() == 1)
8082 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 8083
9c8e10ad 8084 action->SetPosition(position+1);
7fe8059f 8085
5d7836c4 8086 // Set the range we'll need to delete in Undo
9c8e10ad 8087 action->SetRange(wxRichTextRange(position+1, position+1));
7fe8059f 8088
5d7836c4
JS
8089 SubmitAction(action);
8090
8091 success = true;
8092 }
8093 wxTheClipboard->Close();
8094 }
8095 }
39a1c2f2
WS
8096#else
8097 wxUnusedVar(position);
8098#endif
5d7836c4
JS
8099 return success;
8100}
8101
8102/// Can we paste from the clipboard?
8103bool wxRichTextBuffer::CanPasteFromClipboard() const
8104{
7fe8059f 8105 bool canPaste = false;
11ef729d 8106#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 8107 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 8108 {
f7d83f24
VZ
8109 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8110#if wxUSE_UNICODE
603f702b
JS
8111 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8112#endif
8113 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8114 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 8115 {
7fe8059f 8116 canPaste = true;
5d7836c4
JS
8117 }
8118 wxTheClipboard->Close();
8119 }
39a1c2f2 8120#endif
5d7836c4
JS
8121 return canPaste;
8122}
8123
8124/// Dumps contents of buffer for debugging purposes
8125void wxRichTextBuffer::Dump()
8126{
8127 wxString text;
8128 {
8129 wxStringOutputStream stream(& text);
8130 wxTextOutputStream textStream(stream);
8131 Dump(textStream);
8132 }
8133
8134 wxLogDebug(text);
8135}
8136
d2d0adc7
JS
8137/// Add an event handler
8138bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8139{
8140 m_eventHandlers.Append(handler);
8141 return true;
8142}
8143
8144/// Remove an event handler
8145bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8146{
8147 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8148 if (node)
8149 {
8150 m_eventHandlers.Erase(node);
8151 if (deleteHandler)
8152 delete handler;
3e541562 8153
d2d0adc7
JS
8154 return true;
8155 }
8156 else
8157 return false;
8158}
8159
8160/// Clear event handlers
8161void wxRichTextBuffer::ClearEventHandlers()
8162{
8163 m_eventHandlers.Clear();
8164}
8165
8166/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8167/// otherwise will stop at the first successful one.
8168bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8169{
8170 bool success = false;
8171 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8172 {
8173 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8174 if (handler->ProcessEvent(event))
8175 {
8176 success = true;
8177 if (!sendToAll)
8178 return true;
8179 }
8180 }
8181 return success;
8182}
8183
8184/// Set style sheet and notify of the change
8185bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8186{
8187 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
3e541562 8188
616c7cbd 8189 wxWindowID winid = wxID_ANY;
d2d0adc7 8190 if (GetRichTextCtrl())
616c7cbd 8191 winid = GetRichTextCtrl()->GetId();
3e541562 8192
616c7cbd 8193 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
d2d0adc7 8194 event.SetEventObject(GetRichTextCtrl());
603f702b 8195 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
d2d0adc7
JS
8196 event.SetOldStyleSheet(oldSheet);
8197 event.SetNewStyleSheet(sheet);
8198 event.Allow();
3e541562 8199
d2d0adc7
JS
8200 if (SendEvent(event) && !event.IsAllowed())
8201 {
8202 if (sheet != oldSheet)
8203 delete sheet;
8204
8205 return false;
8206 }
8207
8208 if (oldSheet && oldSheet != sheet)
8209 delete oldSheet;
8210
8211 SetStyleSheet(sheet);
8212
8213 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8214 event.SetOldStyleSheet(NULL);
8215 event.Allow();
8216
8217 return SendEvent(event);
8218}
8219
8220/// Set renderer, deleting old one
8221void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8222{
8223 if (sm_renderer)
8224 delete sm_renderer;
8225 sm_renderer = renderer;
8226}
8227
603f702b
JS
8228/// Hit-testing: returns a flag indicating hit test details, plus
8229/// information about position
8db2e3ef 8230int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
603f702b 8231{
8db2e3ef 8232 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
603f702b
JS
8233 if (ret != wxRICHTEXT_HITTEST_NONE)
8234 {
8235 return ret;
8236 }
8237 else
8238 {
8239 textPosition = m_ownRange.GetEnd()-1;
8240 *obj = this;
8241 *contextObj = this;
8242 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8243 }
8244}
8245
32423dd8
JS
8246void wxRichTextBuffer::SetFontScale(double fontScale)
8247{
8248 m_fontScale = fontScale;
8249 m_fontTable.SetFontScale(fontScale);
8250}
8251
8252void wxRichTextBuffer::SetDimensionScale(double dimScale)
8253{
8254 m_dimensionScale = dimScale;
8255}
8256
24777478 8257bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
d2d0adc7 8258{
a1b806b9 8259 if (bulletAttr.GetTextColour().IsOk())
d2d0adc7 8260 {
ecb5fbf1
JS
8261 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8262 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
d2d0adc7
JS
8263 }
8264 else
8265 {
ecb5fbf1
JS
8266 wxCheckSetPen(dc, *wxBLACK_PEN);
8267 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
d2d0adc7
JS
8268 }
8269
8270 wxFont font;
44cc96a8
JS
8271 if (bulletAttr.HasFont())
8272 {
8273 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8274 }
d2d0adc7
JS
8275 else
8276 font = (*wxNORMAL_FONT);
8277
ecb5fbf1 8278 wxCheckSetFont(dc, font);
d2d0adc7
JS
8279
8280 int charHeight = dc.GetCharHeight();
3e541562 8281
d2d0adc7
JS
8282 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8283 int bulletHeight = bulletWidth;
8284
8285 int x = rect.x;
3e541562 8286
d2d0adc7
JS
8287 // Calculate the top position of the character (as opposed to the whole line height)
8288 int y = rect.y + (rect.height - charHeight);
3e541562 8289
d2d0adc7
JS
8290 // Calculate where the bullet should be positioned
8291 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
3e541562 8292
d2d0adc7 8293 // The margin between a bullet and text.
44219ff0 8294 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8295
d2d0adc7
JS
8296 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8297 x = rect.x + rect.width - bulletWidth - margin;
8298 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8299 x = x + (rect.width)/2 - bulletWidth/2;
3e541562 8300
d2d0adc7
JS
8301 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8302 {
8303 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8304 }
8305 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8306 {
8307 wxPoint pts[5];
8308 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8309 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8310 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8311 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
3e541562 8312
d2d0adc7
JS
8313 dc.DrawPolygon(4, pts);
8314 }
8315 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8316 {
8317 wxPoint pts[3];
8318 pts[0].x = x; pts[0].y = y;
8319 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8320 pts[2].x = x; pts[2].y = y + bulletHeight;
3e541562 8321
d2d0adc7
JS
8322 dc.DrawPolygon(3, pts);
8323 }
603f702b
JS
8324 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8325 {
8326 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8327 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8328 }
d2d0adc7
JS
8329 else // "standard/circle", and catch-all
8330 {
8331 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
3e541562
JS
8332 }
8333
d2d0adc7
JS
8334 return true;
8335}
8336
24777478 8337bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
8338{
8339 if (!text.empty())
8340 {
8341 wxFont font;
44cc96a8
JS
8342 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8343 {
24777478 8344 wxRichTextAttr fontAttr;
32423dd8
JS
8345 if (attr.HasFontPixelSize())
8346 fontAttr.SetFontPixelSize(attr.GetFontSize());
8347 else
8348 fontAttr.SetFontPointSize(attr.GetFontSize());
44cc96a8
JS
8349 fontAttr.SetFontStyle(attr.GetFontStyle());
8350 fontAttr.SetFontWeight(attr.GetFontWeight());
8351 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8352 fontAttr.SetFontFaceName(attr.GetBulletFont());
8353 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8354 }
8355 else if (attr.HasFont())
8356 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
d2d0adc7
JS
8357 else
8358 font = (*wxNORMAL_FONT);
8359
ecb5fbf1 8360 wxCheckSetFont(dc, font);
d2d0adc7 8361
a1b806b9 8362 if (attr.GetTextColour().IsOk())
d2d0adc7
JS
8363 dc.SetTextForeground(attr.GetTextColour());
8364
04ee05f9 8365 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
d2d0adc7
JS
8366
8367 int charHeight = dc.GetCharHeight();
8368 wxCoord tw, th;
8369 dc.GetTextExtent(text, & tw, & th);
8370
8371 int x = rect.x;
8372
8373 // Calculate the top position of the character (as opposed to the whole line height)
3e541562 8374 int y = rect.y + (rect.height - charHeight);
d2d0adc7
JS
8375
8376 // The margin between a bullet and text.
44219ff0 8377 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
3e541562 8378
d2d0adc7
JS
8379 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8380 x = (rect.x + rect.width) - tw - margin;
8381 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8382 x = x + (rect.width)/2 - tw/2;
8383
8384 dc.DrawText(text, x, y);
3e541562 8385
d2d0adc7
JS
8386 return true;
8387 }
8388 else
8389 return false;
8390}
8391
24777478 8392bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
d2d0adc7
JS
8393{
8394 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8395 // with the buffer. The store will allow retrieval from memory, disk or other means.
8396 return false;
8397}
8398
8399/// Enumerate the standard bullet names currently supported
8400bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8401{
04529b2a 8402 bulletNames.Add(wxTRANSLATE("standard/circle"));
603f702b 8403 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
04529b2a
JS
8404 bulletNames.Add(wxTRANSLATE("standard/square"));
8405 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8406 bulletNames.Add(wxTRANSLATE("standard/triangle"));
d2d0adc7
JS
8407
8408 return true;
8409}
5d7836c4 8410
bec80f4f
JS
8411/*!
8412 * wxRichTextBox
8413 */
8414
603f702b 8415IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
bec80f4f
JS
8416
8417wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
603f702b 8418 wxRichTextParagraphLayoutBox(parent)
bec80f4f
JS
8419{
8420}
8421
8422/// Draw the item
8db2e3ef 8423bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
bec80f4f 8424{
603f702b
JS
8425 if (!IsShown())
8426 return true;
5ad9ae3a 8427
603f702b
JS
8428 // TODO: if the active object in the control, draw an indication.
8429 // We need to add the concept of active object, and not just focus object,
8430 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8431 // Ultimately we would like to be able to interactively resize an active object
8432 // using drag handles.
8db2e3ef 8433 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b 8434}
5ad9ae3a 8435
603f702b
JS
8436/// Copy
8437void wxRichTextBox::Copy(const wxRichTextBox& obj)
8438{
8439 wxRichTextParagraphLayoutBox::Copy(obj);
8440}
8441
8442// Edit properties via a GUI
8443bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8444{
8445 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8446 boxDlg.SetAttributes(GetAttributes());
8447
8448 if (boxDlg.ShowModal() == wxID_OK)
8449 {
8450 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8451 // indeterminate in the object.
8452 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8453 return true;
5ad9ae3a 8454 }
603f702b
JS
8455 else
8456 return false;
bec80f4f
JS
8457}
8458
7c9fdebe
JS
8459/*!
8460 * wxRichTextField
8461 */
8462
8463IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8464
8465wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8466 wxRichTextParagraphLayoutBox(parent)
8467{
8468 SetFieldType(fieldType);
8469}
8470
8471/// Draw the item
8472bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8473{
8474 if (!IsShown())
8475 return true;
8476
8477 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8478 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8479 return true;
8480
8481 // Fallback; but don't draw guidelines.
8482 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8483 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8484}
8485
8486bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8487{
8488 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8489 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8490 return true;
8491
8492 // Fallback
8493 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8494}
8495
8496bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8497{
8498 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8499 if (fieldType)
8500 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents);
8501
8502 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
8503}
8504
8505/// Calculate range
8506void wxRichTextField::CalculateRange(long start, long& end)
8507{
8508 if (IsTopLevel())
8509 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8510 else
8511 wxRichTextObject::CalculateRange(start, end);
8512}
8513
8514/// Copy
8515void wxRichTextField::Copy(const wxRichTextField& obj)
8516{
8517 wxRichTextParagraphLayoutBox::Copy(obj);
8518
32423dd8 8519 UpdateField(GetBuffer());
7c9fdebe
JS
8520}
8521
8522// Edit properties via a GUI
8523bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8524{
8525 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8526 if (fieldType)
8527 return fieldType->EditProperties(this, parent, buffer);
8528
8529 return false;
8530}
8531
8532bool wxRichTextField::CanEditProperties() const
8533{
8534 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8535 if (fieldType)
8536 return fieldType->CanEditProperties((wxRichTextField*) this);
8537
8538 return false;
8539}
8540
8541wxString wxRichTextField::GetPropertiesMenuLabel() const
8542{
8543 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8544 if (fieldType)
8545 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8546
8547 return wxEmptyString;
8548}
8549
32423dd8 8550bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
7c9fdebe
JS
8551{
8552 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8553 if (fieldType)
32423dd8 8554 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
7c9fdebe
JS
8555
8556 return false;
8557}
8558
8559bool wxRichTextField::IsTopLevel() const
8560{
8561 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8562 if (fieldType)
8563 return fieldType->IsTopLevel((wxRichTextField*) this);
8564
8565 return true;
8566}
8567
8568IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8569
8570IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8571
8572wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8573{
8574 Init();
8575
8576 SetName(name);
8577 SetLabel(label);
8578 SetDisplayStyle(displayStyle);
8579}
8580
8581wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8582{
8583 Init();
8584
8585 SetName(name);
8586 SetBitmap(bitmap);
8587 SetDisplayStyle(displayStyle);
8588}
8589
8590void wxRichTextFieldTypeStandard::Init()
8591{
8592 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8593 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8594 m_textColour = *wxWHITE;
8595 m_borderColour = *wxBLACK;
8596 m_backgroundColour = *wxBLACK;
8597 m_verticalPadding = 1;
8598 m_horizontalPadding = 3;
8599 m_horizontalMargin = 2;
8600 m_verticalMargin = 0;
8601}
8602
8603void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
8604{
8605 wxRichTextFieldType::Copy(field);
8606
8607 m_label = field.m_label;
8608 m_displayStyle = field.m_displayStyle;
8609 m_font = field.m_font;
8610 m_textColour = field.m_textColour;
8611 m_borderColour = field.m_borderColour;
8612 m_backgroundColour = field.m_backgroundColour;
8613 m_verticalPadding = field.m_verticalPadding;
8614 m_horizontalPadding = field.m_horizontalPadding;
8615 m_horizontalMargin = field.m_horizontalMargin;
8616 m_bitmap = field.m_bitmap;
8617}
8618
8619bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
8620{
8621 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8622 return false; // USe default composite drawing
8623 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8624 {
8625 int borderSize = 1;
8626
8627 wxPen borderPen(m_borderColour, 1, wxSOLID);
8628 wxBrush backgroundBrush(m_backgroundColour);
8629 wxColour textColour(m_textColour);
8630
8631 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8632 {
8633 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
8634 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
8635
8636 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
8637 backgroundBrush = wxBrush(highlightColour);
8638
8639 wxCheckSetBrush(dc, backgroundBrush);
8640 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
8641 dc.DrawRectangle(rect);
8642 }
8643
8644 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
8645 borderSize = 0;
8646
8647 // objectRect is the area where the content is drawn, after margins around it have been taken into account
8648 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
8649 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
8650
8651 // clientArea is where the text is actually written
8652 wxRect clientArea = objectRect;
8653
8654 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
8655 {
8656 dc.SetPen(borderPen);
8657 dc.SetBrush(backgroundBrush);
8658 dc.DrawRoundedRectangle(objectRect, 4.0);
8659 }
8660 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
8661 {
8662 int arrowLength = objectRect.height/2;
8663 clientArea.width -= (arrowLength - m_horizontalPadding);
8664
8665 wxPoint pts[5];
8666 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
8667 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
8668 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
8669 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
8670 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
8671 dc.SetPen(borderPen);
8672 dc.SetBrush(backgroundBrush);
8673 dc.DrawPolygon(5, pts);
8674 }
8675 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
8676 {
8677 int arrowLength = objectRect.height/2;
8678 clientArea.width -= (arrowLength - m_horizontalPadding);
8679 clientArea.x += (arrowLength - m_horizontalPadding);
8680
8681 wxPoint pts[5];
8682 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
8683 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
8684 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
8685 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
8686 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
8687 dc.SetPen(borderPen);
8688 dc.SetBrush(backgroundBrush);
8689 dc.DrawPolygon(5, pts);
8690 }
8691
8692 if (m_bitmap.IsOk())
8693 {
8694 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
8695 int y = clientArea.y + m_verticalPadding;
8696 dc.DrawBitmap(m_bitmap, x, y, true);
8697
8698 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8699 {
8700 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8701 wxCheckSetPen(dc, *wxBLACK_PEN);
8702 dc.SetLogicalFunction(wxINVERT);
8703 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
8704 dc.SetLogicalFunction(wxCOPY);
8705 }
8706 }
8707 else
8708 {
8709 wxString label(m_label);
8710 if (label.IsEmpty())
8711 label = wxT("??");
8712 int w, h, maxDescent;
8713 dc.SetFont(m_font);
8714 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
8715 dc.SetTextForeground(textColour);
8716
8717 int x = clientArea.x + (clientArea.width - w)/2;
8718 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
8719 dc.DrawText(m_label, x, y);
8720 }
8721 }
8722
8723 return true;
8724}
8725
8726bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
8727{
8728 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8729 return false; // USe default composite layout
8730
8731 wxSize size = GetSize(obj, dc, context, style);
8732 obj->SetCachedSize(size);
8733 obj->SetMinSize(size);
8734 obj->SetMaxSize(size);
8735 return true;
8736}
8737
8738bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8739{
8740 if (IsTopLevel(obj))
8741 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position);
8742 else
8743 {
8744 wxSize sz = GetSize(obj, dc, context, 0);
8745 if (partialExtents)
8746 {
8747 int lastSize;
8748 if (partialExtents->GetCount() > 0)
8749 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
8750 else
8751 lastSize = 0;
8752 partialExtents->Add(lastSize + sz.x);
8753 }
8754 size = sz;
8755 return true;
8756 }
8757}
8758
8759wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
8760{
8761 int borderSize = 1;
8762 int w = 0, h = 0, maxDescent = 0;
8763
8764 wxSize sz;
8765 if (m_bitmap.IsOk())
8766 {
8767 w = m_bitmap.GetWidth();
8768 h = m_bitmap.GetHeight();
8769
8770 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
8771 }
8772 else
8773 {
8774 wxString label(m_label);
8775 if (label.IsEmpty())
8776 label = wxT("??");
8777 dc.SetFont(m_font);
8778 dc.GetTextExtent(label, & w, &h, & maxDescent);
8779
8780 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
8781 }
8782
8783 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
8784 {
8785 sz.x += borderSize*2;
8786 sz.y += borderSize*2;
8787 }
8788
8789 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
8790 {
8791 // Add space for the arrow
8792 sz.x += (sz.y/2 - m_horizontalPadding);
8793 }
8794
8795 return sz;
8796}
8797
603f702b
JS
8798IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
8799
8800wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
8801 wxRichTextBox(parent)
bec80f4f 8802{
603f702b
JS
8803}
8804
8805/// Draw the item
8db2e3ef 8806bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 8807{
8db2e3ef 8808 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
603f702b
JS
8809}
8810
8811/// Copy
8812void wxRichTextCell::Copy(const wxRichTextCell& obj)
8813{
8814 wxRichTextBox::Copy(obj);
8815}
8816
8817// Edit properties via a GUI
8818bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8819{
8820 // We need to gather common attributes for all selected cells.
8821
8822 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
8823 bool multipleCells = false;
8824 wxRichTextAttr attr;
8825
8826 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
8827 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
5ad9ae3a 8828 {
603f702b
JS
8829 wxRichTextAttr clashingAttr, absentAttr;
8830 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8831 size_t i;
8832 int selectedCellCount = 0;
8833 for (i = 0; i < sel.GetCount(); i++)
8834 {
8835 const wxRichTextRange& range = sel[i];
8836 wxRichTextCell* cell = table->GetCell(range.GetStart());
8837 if (cell)
8838 {
8839 wxRichTextAttr cellStyle = cell->GetAttributes();
5ad9ae3a 8840
603f702b
JS
8841 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
8842
8843 selectedCellCount ++;
8844 }
8845 }
8846 multipleCells = selectedCellCount > 1;
5ad9ae3a 8847 }
603f702b
JS
8848 else
8849 {
8850 attr = GetAttributes();
8851 }
8852
8853 wxString caption;
8854 if (multipleCells)
8855 caption = _("Multiple Cell Properties");
8856 else
8857 caption = _("Cell Properties");
8858
8859 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
8860 cellDlg.SetAttributes(attr);
8861
80a46597 8862 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
603f702b
JS
8863 if (sizePage)
8864 {
8865 // We don't want position and floating controls for a cell.
8866 sizePage->ShowPositionControls(false);
8867 sizePage->ShowFloatingControls(false);
8868 }
8869
8870 if (cellDlg.ShowModal() == wxID_OK)
8871 {
8872 if (multipleCells)
8873 {
8874 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8875 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8876 // since it may represent clashing attributes across multiple objects.
8877 table->SetCellStyle(sel, attr);
8878 }
8879 else
8880 // For a single object, indeterminate attributes set by the user should be reflected in the
8881 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8882 // the style directly instead of applying (which ignores indeterminate attributes,
8883 // leaving them as they were).
8884 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8885 return true;
8886 }
8887 else
8888 return false;
8889}
8890
8891WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
8892
8893IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
8894
8895wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
8896{
8897 m_rowCount = 0;
8898 m_colCount = 0;
8899}
5ad9ae3a 8900
603f702b 8901// Draws the object.
8db2e3ef 8902bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
603f702b 8903{
8db2e3ef 8904 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
bec80f4f
JS
8905}
8906
603f702b
JS
8907WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
8908WX_DEFINE_OBJARRAY(wxRichTextRectArray);
8909
8910// Lays the object out. rect is the space available for layout. Often it will
8911// be the specified overall space for this object, if trying to constrain
8912// layout to a particular size, or it could be the total space available in the
8913// parent. rect is the overall size, so we must subtract margins and padding.
8914// to get the actual available space.
8db2e3ef 8915bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
bec80f4f 8916{
603f702b
JS
8917 SetPosition(rect.GetPosition());
8918
8919 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8920 // minimum size if within alloted size, then divide up remaining size
8921 // between rows/cols.
8922
8923 double scale = 1.0;
8924 wxRichTextBuffer* buffer = GetBuffer();
8925 if (buffer) scale = buffer->GetScale();
8926
8db2e3ef 8927 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
603f702b
JS
8928 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
8929
8db2e3ef
JS
8930 wxRichTextAttr attr(GetAttributes());
8931 context.ApplyVirtualAttributes(attr, this);
8932
603f702b
JS
8933 // If we have no fixed table size, and assuming we're not pushed for
8934 // space, then we don't have to try to stretch the table to fit the contents.
8935 bool stretchToFitTableWidth = false;
8936
8937 int tableWidth = rect.width;
8db2e3ef 8938 if (attr.GetTextBoxAttr().GetWidth().IsValid())
603f702b 8939 {
8db2e3ef 8940 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
603f702b
JS
8941
8942 // Fixed table width, so we do want to stretch columns out if necessary.
8943 stretchToFitTableWidth = true;
8944
8945 // Shouldn't be able to exceed the size passed to this function
8946 tableWidth = wxMin(rect.width, tableWidth);
8947 }
8948
8949 // Get internal padding
36307fdf 8950 int paddingLeft = 0, paddingTop = 0;
8db2e3ef
JS
8951 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8952 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
8953 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
8954 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
603f702b
JS
8955
8956 // Assume that left and top padding are also used for inter-cell padding.
8957 int paddingX = paddingLeft;
8958 int paddingY = paddingTop;
8959
8960 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8db2e3ef 8961 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
603f702b
JS
8962
8963 // Internal table width - the area for content
8964 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
8965
8966 int rowCount = m_cells.GetCount();
8967 if (m_colCount == 0 || rowCount == 0)
8968 {
8969 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
8970 SetCachedSize(overallRect.GetSize());
8971
8972 // Zero content size
8973 SetMinSize(overallRect.GetSize());
8974 SetMaxSize(GetMinSize());
8975 return true;
8976 }
8977
8978 // The final calculated widths
bb7bbd12
JS
8979 wxArrayInt colWidths;
8980 colWidths.Add(0, m_colCount);
603f702b 8981
bb7bbd12
JS
8982 wxArrayInt absoluteColWidths;
8983 absoluteColWidths.Add(0, m_colCount);
7c9fdebe 8984
bb7bbd12
JS
8985 wxArrayInt percentageColWidths;
8986 percentageColWidths.Add(0, m_colCount);
603f702b
JS
8987 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8988 // These are only relevant when the first column contains spanning information.
8989 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
bb7bbd12
JS
8990 wxArrayInt maxColWidths;
8991 maxColWidths.Add(0, m_colCount);
8992 wxArrayInt minColWidths;
8993 minColWidths.Add(0, m_colCount);
603f702b
JS
8994
8995 wxSize tableSize(tableWidth, 0);
8996
8997 int i, j, k;
8998
8999 for (i = 0; i < m_colCount; i++)
9000 {
9001 absoluteColWidths[i] = 0;
9002 // absoluteColWidthsSpanning[i] = 0;
9003 percentageColWidths[i] = -1;
9004 // percentageColWidthsSpanning[i] = -1;
9005 colWidths[i] = 0;
9006 maxColWidths[i] = 0;
9007 minColWidths[i] = 0;
9008 // columnSpans[i] = 1;
9009 }
9010
9011 // (0) Determine which cells are visible according to spans
9012 // 1 2 3 4 5
9013 // __________________
9014 // | | | | | 1
9015 // |------| |----|
9016 // |------| | | 2
9017 // |------| | | 3
9018 // |------------------|
9019 // |__________________| 4
9020
9021 // To calculate cell visibility:
9022 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9023 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9024 // that cell, hide the cell.
9025
9026 // We can also use this array to match the size of spanning cells to the grid. Or just do
9027 // this when we iterate through all cells.
9028
9029 // 0.1: add spanning cells to an array
9030 wxRichTextRectArray rectArray;
9031 for (j = 0; j < m_rowCount; j++)
9032 {
9033 for (i = 0; i < m_colCount; i++)
9034 {
9035 wxRichTextBox* cell = GetCell(j, i);
9036 int colSpan = 1, rowSpan = 1;
9037 if (cell->GetProperties().HasProperty(wxT("colspan")))
9038 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9039 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9040 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9041 if (colSpan > 1 || rowSpan > 1)
9042 {
9043 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9044 }
9045 }
9046 }
9047 // 0.2: find which cells are subsumed by a spanning cell
9048 for (j = 0; j < m_rowCount; j++)
9049 {
9050 for (i = 0; i < m_colCount; i++)
9051 {
9052 wxRichTextBox* cell = GetCell(j, i);
9053 if (rectArray.GetCount() == 0)
9054 {
9055 cell->Show(true);
9056 }
9057 else
9058 {
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 // Assume all spanning cells are shown
9067 cell->Show(true);
9068 }
9069 else
9070 {
9071 bool shown = true;
9072 for (k = 0; k < (int) rectArray.GetCount(); k++)
9073 {
9074 if (rectArray[k].Contains(wxPoint(i, j)))
9075 {
9076 shown = false;
9077 break;
9078 }
9079 }
9080 cell->Show(shown);
9081 }
9082 }
9083 }
9084 }
9085
9086 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9087 // overlap with a spanned cell starting at a previous column position.
9088 // This means we need to keep an array of rects so we can check. However
9089 // it does also mean that some spans simply may not be taken into account
9090 // where there are different spans happening on different rows. In these cases,
9091 // they will simply be as wide as their constituent columns.
9092
9093 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9094 // the absolute or percentage width of each column.
9095
9096 for (j = 0; j < m_rowCount; j++)
9097 {
9098 // First get the overall margins so we can calculate percentage widths based on
9099 // the available content space for all cells on the row
9100
9101 int overallRowContentMargin = 0;
9102 int visibleCellCount = 0;
9103
9104 for (i = 0; i < m_colCount; i++)
9105 {
9106 wxRichTextBox* cell = GetCell(j, i);
9107 if (cell->IsShown())
9108 {
9109 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9110 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9111
9112 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9113 visibleCellCount ++;
9114 }
9115 }
9116
9117 // Add in inter-cell padding
9118 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9119
9120 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9121 wxSize rowTableSize(rowContentWidth, 0);
9122 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9123
9124 for (i = 0; i < m_colCount; i++)
9125 {
9126 wxRichTextBox* cell = GetCell(j, i);
9127 if (cell->IsShown())
9128 {
9129 int colSpan = 1;
9130 if (cell->GetProperties().HasProperty(wxT("colspan")))
9131 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9132
9133 // Lay out cell to find min/max widths
9134 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9135 cell->Layout(dc, context, availableSpace, availableSpace, style);
603f702b
JS
9136
9137 if (colSpan == 1)
9138 {
9139 int absoluteCellWidth = -1;
9140 int percentageCellWidth = -1;
9141
9142 // I think we need to calculate percentages from the internal table size,
9143 // minus the padding between cells which we'll need to calculate from the
9144 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9145 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9146 // so if we want to conform to that we'll need to add in the overall cell margins.
9147 // However, this will make it difficult to specify percentages that add up to
9148 // 100% and still fit within the table width.
9149 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9150 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9151 // If we're using internal content size for the width, we would calculate the
9152 // the overall cell width for n cells as:
9153 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9154 // + thisOverallCellMargin
9155 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9156 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9157
9158 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9159 {
9160 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9161 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9162 {
9163 percentageCellWidth = w;
9164 }
9165 else
9166 {
9167 absoluteCellWidth = w;
9168 }
9169 // Override absolute width with minimum width if necessary
9170 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9171 absoluteCellWidth = cell->GetMinSize().x;
9172 }
9173
9174 if (absoluteCellWidth != -1)
9175 {
9176 if (absoluteCellWidth > absoluteColWidths[i])
9177 absoluteColWidths[i] = absoluteCellWidth;
9178 }
9179
9180 if (percentageCellWidth != -1)
9181 {
9182 if (percentageCellWidth > percentageColWidths[i])
9183 percentageColWidths[i] = percentageCellWidth;
9184 }
9185
9186 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9187 minColWidths[i] = cell->GetMinSize().x;
9188 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9189 maxColWidths[i] = cell->GetMaxSize().x;
9190 }
9191 }
9192 }
9193 }
9194
9195 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9196 // TODO: simply merge this into (1).
9197 for (i = 0; i < m_colCount; i++)
9198 {
9199 if (absoluteColWidths[i] > 0)
9200 {
9201 colWidths[i] = absoluteColWidths[i];
9202 }
9203 else if (percentageColWidths[i] > 0)
9204 {
9205 colWidths[i] = percentageColWidths[i];
9206
9207 // This is rubbish - we calculated the absolute widths from percentages, so
9208 // we can't do it again here.
9209 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9210 }
9211 }
9212
9213 // (3) Process absolute or proportional widths of spanning columns,
9214 // now that we know what our fixed column widths are going to be.
9215 // Spanned cells will try to adjust columns so the span will fit.
9216 // Even existing fixed column widths can be expanded if necessary.
9217 // Actually, currently fixed columns widths aren't adjusted; instead,
9218 // the algorithm favours earlier rows and adjusts unspecified column widths
9219 // the first time only. After that, we can't know whether the column has been
9220 // specified explicitly or not. (We could make a note if necessary.)
9221 for (j = 0; j < m_rowCount; j++)
9222 {
9223 // First get the overall margins so we can calculate percentage widths based on
9224 // the available content space for all cells on the row
9225
9226 int overallRowContentMargin = 0;
9227 int visibleCellCount = 0;
9228
9229 for (i = 0; i < m_colCount; i++)
9230 {
9231 wxRichTextBox* cell = GetCell(j, i);
9232 if (cell->IsShown())
9233 {
9234 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9235 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9236
9237 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9238 visibleCellCount ++;
9239 }
9240 }
9241
9242 // Add in inter-cell padding
9243 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9244
9245 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9246 wxSize rowTableSize(rowContentWidth, 0);
9247 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9248
9249 for (i = 0; i < m_colCount; i++)
9250 {
9251 wxRichTextBox* cell = GetCell(j, i);
9252 if (cell->IsShown())
9253 {
9254 int colSpan = 1;
9255 if (cell->GetProperties().HasProperty(wxT("colspan")))
9256 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9257
9258 if (colSpan > 1)
9259 {
9260 int spans = wxMin(colSpan, m_colCount - i);
9261 int cellWidth = 0;
9262 if (spans > 0)
9263 {
9264 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9265 {
9266 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9267 // Override absolute width with minimum width if necessary
9268 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9269 cellWidth = cell->GetMinSize().x;
9270 }
9271 else
9272 {
9273 // Do we want to do this? It's the only chance we get to
9274 // use the cell's min/max sizes, so we need to work out
9275 // how we're going to balance the unspecified spanning cell
9276 // width with the possibility more-constrained constituent cell widths.
9277 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9278 // don't want to constraint all the spanned columns to fit into this cell.
9279 // OK, let's say that if any of the constituent columns don't fit,
9280 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9281 // cells to the columns later.
9282 cellWidth = cell->GetMinSize().x;
9283 if (cell->GetMaxSize().x > cellWidth)
9284 cellWidth = cell->GetMaxSize().x;
9285 }
9286
9287 // Subtract the padding between cells
9288 int spanningWidth = cellWidth;
9289 spanningWidth -= paddingX * (spans-1);
9290
9291 if (spanningWidth > 0)
9292 {
9293 // Now share the spanning width between columns within that span
9294 // TODO: take into account min widths of columns within the span
9295 int spanningWidthLeft = spanningWidth;
9296 int stretchColCount = 0;
9297 for (k = i; k < (i+spans); k++)
9298 {
9299 if (colWidths[k] > 0) // absolute or proportional width has been specified
9300 spanningWidthLeft -= colWidths[k];
9301 else
9302 stretchColCount ++;
9303 }
9304 // Now divide what's left between the remaining columns
9305 int colShare = 0;
9306 if (stretchColCount > 0)
9307 colShare = spanningWidthLeft / stretchColCount;
9308 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9309
9310 // If fixed-width columns are currently too big, then we'll later
9311 // stretch the spanned cell to fit.
9312
9313 if (spanningWidthLeft > 0)
9314 {
9315 for (k = i; k < (i+spans); k++)
9316 {
9317 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9318 {
9319 int newWidth = colShare;
9320 if (k == (i+spans-1))
9321 newWidth += colShareRemainder; // ensure all pixels are filled
9322 colWidths[k] = newWidth;
9323 }
9324 }
9325 }
9326 }
9327 }
9328 }
9329 }
9330 }
9331 }
9332
9333 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9334 // TODO: take into account min widths of columns within the span
9335 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9336 int widthLeft = tableWidthMinusPadding;
9337 int stretchColCount = 0;
9338 for (i = 0; i < m_colCount; i++)
9339 {
9340 // TODO: we need to take into account min widths.
9341 // Subtract min width from width left, then
9342 // add the colShare to the min width
9343 if (colWidths[i] > 0) // absolute or proportional width has been specified
9344 widthLeft -= colWidths[i];
9345 else
9346 {
9347 if (minColWidths[i] > 0)
9348 widthLeft -= minColWidths[i];
9349
9350 stretchColCount ++;
9351 }
9352 }
9353
9354 // Now divide what's left between the remaining columns
9355 int colShare = 0;
9356 if (stretchColCount > 0)
9357 colShare = widthLeft / stretchColCount;
9358 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9359
9360 // Check we don't have enough space, in which case shrink all columns, overriding
9361 // any absolute/proportional widths
9362 // TODO: actually we would like to divide up the shrinkage according to size.
9363 // How do we calculate the proportions that will achieve this?
9364 // Could first choose an arbitrary value for stretching cells, and then calculate
9365 // factors to multiply each width by.
9366 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9367 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9368 {
9369 colShare = tableWidthMinusPadding / m_colCount;
9370 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9371 for (i = 0; i < m_colCount; i++)
9372 {
9373 colWidths[i] = 0;
9374 minColWidths[i] = 0;
9375 }
9376 }
9377
9378 // We have to adjust the columns if either we need to shrink the
9379 // table to fit the parent/table width, or we explicitly set the
9380 // table width and need to stretch out the table.
9381 if (widthLeft < 0 || stretchToFitTableWidth)
9382 {
9383 for (i = 0; i < m_colCount; i++)
9384 {
9385 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9386 {
9387 if (minColWidths[i] > 0)
9388 colWidths[i] = minColWidths[i] + colShare;
9389 else
9390 colWidths[i] = colShare;
9391 if (i == (m_colCount-1))
9392 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9393 }
9394 }
9395 }
9396
9397 // TODO: if spanned cells have no specified or max width, make them the
9398 // as big as the columns they span. Do this for all spanned cells in all
9399 // rows, of course. Size any spanned cells left over at the end - even if they
9400 // have width > 0, make sure they're limited to the appropriate column edge.
9401
9402
9403/*
9404 Sort out confusion between content width
9405 and overall width later. For now, assume we specify overall width.
9406
9407 So, now we've laid out the table to fit into the given space
9408 and have used specified widths and minimum widths.
9409
9410 Now we need to consider how we will try to take maximum width into account.
9411
9412*/
9413
9414 // (??) TODO: take max width into account
9415
9416 // (6) Lay out all cells again with the current values
9417
9418 int maxRight = 0;
9419 int y = availableSpace.y;
9420 for (j = 0; j < m_rowCount; j++)
9421 {
9422 int x = availableSpace.x; // TODO: take into account centering etc.
9423 int maxCellHeight = 0;
9424 int maxSpecifiedCellHeight = 0;
9425
bb7bbd12
JS
9426 wxArrayInt actualWidths;
9427 actualWidths.Add(0, m_colCount);
603f702b
JS
9428
9429 wxTextAttrDimensionConverter converter(dc, scale);
9430 for (i = 0; i < m_colCount; i++)
9431 {
9432 wxRichTextCell* cell = GetCell(j, i);
9433 if (cell->IsShown())
9434 {
603f702b
JS
9435 // Get max specified cell height
9436 // Don't handle percentages for height
9437 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9438 {
9439 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9440 if (h > maxSpecifiedCellHeight)
9441 maxSpecifiedCellHeight = h;
9442 }
9443
9444 if (colWidths[i] > 0) // absolute or proportional width has been specified
9445 {
9446 int colSpan = 1;
9447 if (cell->GetProperties().HasProperty(wxT("colspan")))
9448 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9449
9450 wxRect availableCellSpace;
9451
9452 // TODO: take into acount spans
9453 if (colSpan > 1)
9454 {
9455 // Calculate the size of this spanning cell from its constituent columns
9456 int xx = x;
9457 int spans = wxMin(colSpan, m_colCount - i);
9458 for (k = i; k < spans; k++)
9459 {
9460 if (k != i)
9461 xx += paddingX;
9462 xx += colWidths[k];
9463 }
9464 availableCellSpace = wxRect(x, y, xx, -1);
9465 }
9466 else
9467 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9468
9469 // Store actual width so we can force cell to be the appropriate width on the final loop
9470 actualWidths[i] = availableCellSpace.GetWidth();
9471
9472 // Lay out cell
9473 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9474 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9475
9476 // TODO: use GetCachedSize().x to compute 'natural' size
9477
9478 x += (availableCellSpace.GetWidth() + paddingX);
9479 if (cell->GetCachedSize().y > maxCellHeight)
9480 maxCellHeight = cell->GetCachedSize().y;
9481 }
9482 }
9483 }
9484
9485 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9486
9487 for (i = 0; i < m_colCount; i++)
9488 {
9489 wxRichTextCell* cell = GetCell(j, i);
9490 if (cell->IsShown())
9491 {
9492 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9493 // Lay out cell with new height
9494 cell->Invalidate(wxRICHTEXT_ALL);
8db2e3ef 9495 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
603f702b
JS
9496
9497 // Make sure the cell size really is the appropriate size,
9498 // not the calculated box size
9499 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9500
9501 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9502 }
9503 }
9504
9505 y += maxCellHeight;
9506 if (j < (m_rowCount-1))
9507 y += paddingY;
9508 }
9509
9510 // We need to add back the margins etc.
9511 {
9512 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9513 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8db2e3ef 9514 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
9515 SetCachedSize(marginRect.GetSize());
9516 }
9517
9518 // TODO: calculate max size
9519 {
9520 SetMaxSize(GetCachedSize());
9521 }
9522
9523 // TODO: calculate min size
9524 {
9525 SetMinSize(GetCachedSize());
9526 }
9527
9528 // TODO: currently we use either a fixed table width or the parent's size.
9529 // We also want to be able to calculate the table width from its content,
9530 // whether using fixed column widths or cell content min/max width.
9531 // Probably need a boolean flag to say whether we need to stretch cells
9532 // to fit the table width, or to simply use min/max cell widths. The
9533 // trouble with this is that if cell widths are not specified, they
9534 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9535 // Anyway, ignoring that problem, we probably need to factor layout into a function
9536 // that can can calculate the maximum unconstrained layout in case table size is
9537 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9538 // constrain Layout(), or the previously-calculated max size to constraint layout.
9539
9540 return true;
9541}
9542
9543// Finds the absolute position and row height for the given character position
8db2e3ef 9544bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
603f702b
JS
9545{
9546 wxRichTextCell* child = GetCell(index+1);
9547 if (child)
9548 {
9549 // Find the position at the start of the child cell, since the table doesn't
9550 // have any caret position of its own.
8db2e3ef 9551 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
603f702b
JS
9552 }
9553 else
9554 return false;
9555}
9556
9557// Get the cell at the given character position (in the range of the table).
9558wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9559{
9560 int row = 0, col = 0;
9561 if (GetCellRowColumnPosition(pos, row, col))
9562 {
9563 return GetCell(row, col);
9564 }
9565 else
9566 return NULL;
9567}
9568
9569// Get the row/column for a given character position
9570bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9571{
9572 if (m_colCount == 0 || m_rowCount == 0)
9573 return false;
9574
9575 row = (int) (pos / m_colCount);
9576 col = pos - (row * m_colCount);
9577
9578 wxASSERT(row < m_rowCount && col < m_colCount);
9579
9580 if (row < m_rowCount && col < m_colCount)
9581 return true;
9582 else
9583 return false;
9584}
9585
9586// Calculate range, taking row/cell ordering into account instead of relying
9587// on list ordering.
9588void wxRichTextTable::CalculateRange(long start, long& end)
9589{
9590 long current = start;
9591 long lastEnd = current;
9592
9593 if (IsTopLevel())
9594 {
9595 current = 0;
9596 lastEnd = 0;
9597 }
9598
9599 int i, j;
9600 for (i = 0; i < m_rowCount; i++)
9601 {
9602 for (j = 0; j < m_colCount; j++)
9603 {
9604 wxRichTextCell* child = GetCell(i, j);
9605 if (child)
9606 {
9607 long childEnd = 0;
9608
9609 child->CalculateRange(current, childEnd);
9610
9611 lastEnd = childEnd;
9612 current = childEnd + 1;
9613 }
9614 }
9615 }
9616
9617 // A top-level object always has a range of size 1,
9618 // because its children don't count at this level.
9619 end = start;
9620 m_range.SetRange(start, start);
9621
9622 // An object with no children has zero length
9623 if (m_children.GetCount() == 0)
9624 lastEnd --;
9625 m_ownRange.SetRange(0, lastEnd);
9626}
9627
9628// Gets the range size.
8db2e3ef 9629bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
603f702b 9630{
8db2e3ef 9631 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
603f702b
JS
9632}
9633
9634// Deletes content in the given range.
9635bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
9636{
9637 // TODO: implement deletion of cells
9638 return true;
9639}
9640
9641// Gets any text in this object for the given range.
9642wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
9643{
9644 return wxRichTextBox::GetTextForRange(range);
9645}
9646
9647// Copies this object.
9648void wxRichTextTable::Copy(const wxRichTextTable& obj)
9649{
9650 wxRichTextBox::Copy(obj);
9651
9652 ClearTable();
9653
9654 m_rowCount = obj.m_rowCount;
9655 m_colCount = obj.m_colCount;
9656
9657 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
9658
9659 int i, j;
9660 for (i = 0; i < m_rowCount; i++)
9661 {
9662 wxRichTextObjectPtrArray& colArray = m_cells[i];
9663 for (j = 0; j < m_colCount; j++)
9664 {
9665 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
9666 AppendChild(cell);
9667
9668 colArray.Add(cell);
9669 }
9670 }
9671}
9672
9673void wxRichTextTable::ClearTable()
9674{
9675 m_cells.Clear();
9676 DeleteChildren();
9677}
9678
9679bool wxRichTextTable::CreateTable(int rows, int cols)
9680{
9681 ClearTable();
9682
9683 m_rowCount = rows;
9684 m_colCount = cols;
9685
9686 m_cells.Add(wxRichTextObjectPtrArray(), rows);
9687
9688 int i, j;
9689 for (i = 0; i < rows; i++)
9690 {
9691 wxRichTextObjectPtrArray& colArray = m_cells[i];
9692 for (j = 0; j < cols; j++)
9693 {
9694 wxRichTextCell* cell = new wxRichTextCell;
9695 AppendChild(cell);
9696 cell->AddParagraph(wxEmptyString);
9697
9698 colArray.Add(cell);
9699 }
9700 }
9701
9702 return true;
9703}
9704
9705wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
9706{
9707 wxASSERT(row < m_rowCount);
9708 wxASSERT(col < m_colCount);
9709
9710 if (row < m_rowCount && col < m_colCount)
9711 {
9712 wxRichTextObjectPtrArray& colArray = m_cells[row];
9713 wxRichTextObject* obj = colArray[col];
9714 return wxDynamicCast(obj, wxRichTextCell);
9715 }
9716 else
d67faa04 9717 return NULL;
603f702b
JS
9718}
9719
9720// Returns a selection object specifying the selections between start and end character positions.
9721// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
9722wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
9723{
9724 wxRichTextSelection selection;
9725 selection.SetContainer((wxRichTextTable*) this);
9726
9727 if (start > end)
9728 {
9729 long tmp = end;
9730 end = start;
9731 start = tmp;
9732 }
9733
9734 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
9735
9736 if (end >= (m_colCount * m_rowCount))
9737 return selection;
9738
9739 // We need to find the rectangle of cells that is described by the rectangle
9740 // with start, end as the diagonal. Make sure we don't add cells that are
9741 // not currenty visible because they are overlapped by spanning cells.
9742/*
9743 --------------------------
9744 | 0 | 1 | 2 | 3 | 4 |
9745 --------------------------
9746 | 5 | 6 | 7 | 8 | 9 |
9747 --------------------------
9748 | 10 | 11 | 12 | 13 | 14 |
9749 --------------------------
9750 | 15 | 16 | 17 | 18 | 19 |
9751 --------------------------
9752
9753 Let's say we select 6 -> 18.
9754
9755 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
9756 which is left and which is right.
9757
9758 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
9759
9760 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
9761 and (b) shown.
9762
9763
9764*/
9765
9766 int leftCol = start - m_colCount * int(start/m_colCount);
9767 int rightCol = end - m_colCount * int(end/m_colCount);
9768
9769 int topRow = int(start/m_colCount);
9770 int bottomRow = int(end/m_colCount);
9771
9772 if (leftCol > rightCol)
9773 {
9774 int tmp = rightCol;
9775 rightCol = leftCol;
9776 leftCol = tmp;
9777 }
9778
9779 if (topRow > bottomRow)
9780 {
9781 int tmp = bottomRow;
9782 bottomRow = topRow;
9783 topRow = tmp;
9784 }
9785
9786 int i, j;
9787 for (i = topRow; i <= bottomRow; i++)
9788 {
9789 for (j = leftCol; j <= rightCol; j++)
9790 {
9791 wxRichTextCell* cell = GetCell(i, j);
9792 if (cell && cell->IsShown())
9793 selection.Add(cell->GetRange());
9794 }
9795 }
9796
9797 return selection;
9798}
9799
9800// Sets the attributes for the cells specified by the selection.
9801bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
9802{
9803 if (selection.GetContainer() != this)
9804 return false;
9805
9806 wxRichTextBuffer* buffer = GetBuffer();
9807 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
9808 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
9809
9810 if (withUndo)
9811 buffer->BeginBatchUndo(_("Set Cell Style"));
9812
9813 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
9814 while (node)
9815 {
9816 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
9817 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
9818 SetStyle(cell, style, flags);
9819 node = node->GetNext();
9820 }
9821
9822 // Do action, or delay it until end of batch.
9823 if (withUndo)
9824 buffer->EndBatchUndo();
9825
9826 return true;
9827}
9828
9829bool wxRichTextTable::DeleteRows(int startRow, int noRows)
9830{
9831 wxASSERT((startRow + noRows) < m_rowCount);
9832 if ((startRow + noRows) >= m_rowCount)
9833 return false;
9834
9835 int i, j;
9836 for (i = startRow; i < (startRow+noRows); i++)
9837 {
9838 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
9839 for (j = 0; j < (int) colArray.GetCount(); j++)
9840 {
9841 wxRichTextObject* cell = colArray[j];
9842 RemoveChild(cell, true);
9843 }
9844
9845 // Keep deleting at the same position, since we move all
9846 // the others up
9847 m_cells.RemoveAt(startRow);
9848 }
9849
9850 m_rowCount = m_rowCount - noRows;
9851
9852 return true;
9853}
9854
9855bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
9856{
9857 wxASSERT((startCol + noCols) < m_colCount);
9858 if ((startCol + noCols) >= m_colCount)
9859 return false;
9860
9861 bool deleteRows = (noCols == m_colCount);
9862
9863 int i, j;
9864 for (i = 0; i < m_rowCount; i++)
9865 {
9866 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
9867 for (j = startCol; j < (startCol+noCols); j++)
9868 {
9869 wxRichTextObject* cell = colArray[j];
9870 RemoveChild(cell, true);
9871 }
9872
9873 if (deleteRows)
9874 m_cells.RemoveAt(0);
9875 }
9876
9877 if (deleteRows)
9878 m_rowCount = 0;
9879 m_colCount = m_colCount - noCols;
9880
9881 return true;
9882}
9883
9884bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
9885{
9886 wxASSERT(startRow <= m_rowCount);
9887 if (startRow > m_rowCount)
9888 return false;
9889
9890 int i, j;
9891 for (i = 0; i < noRows; i++)
9892 {
9893 int idx;
9894 if (startRow == m_rowCount)
9895 {
9896 m_cells.Add(wxRichTextObjectPtrArray());
9897 idx = m_cells.GetCount() - 1;
9898 }
9899 else
9900 {
9901 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
9902 idx = startRow+i;
9903 }
9904
9905 wxRichTextObjectPtrArray& colArray = m_cells[idx];
9906 for (j = 0; j < m_colCount; j++)
9907 {
9908 wxRichTextCell* cell = new wxRichTextCell;
9909 cell->GetAttributes() = attr;
9910
9911 AppendChild(cell);
9912 colArray.Add(cell);
9913 }
9914 }
9915
9916 m_rowCount = m_rowCount + noRows;
9917 return true;
9918}
9919
9920bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
9921{
9922 wxASSERT(startCol <= m_colCount);
9923 if (startCol > m_colCount)
9924 return false;
9925
9926 int i, j;
9927 for (i = 0; i < m_rowCount; i++)
9928 {
9929 wxRichTextObjectPtrArray& colArray = m_cells[i];
9930 for (j = 0; j < noCols; j++)
9931 {
9932 wxRichTextCell* cell = new wxRichTextCell;
9933 cell->GetAttributes() = attr;
9934
9935 AppendChild(cell);
9936
9937 if (startCol == m_colCount)
9938 colArray.Add(cell);
9939 else
9940 colArray.Insert(cell, startCol+j);
9941 }
9942 }
9943
9944 m_colCount = m_colCount + noCols;
9945
9946 return true;
5ad9ae3a
JS
9947}
9948
603f702b
JS
9949// Edit properties via a GUI
9950bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
5ad9ae3a 9951{
603f702b
JS
9952 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
9953 boxDlg.SetAttributes(GetAttributes());
9954
9955 if (boxDlg.ShowModal() == wxID_OK)
5ad9ae3a 9956 {
603f702b
JS
9957 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9958 return true;
5ad9ae3a
JS
9959 }
9960 else
9961 return false;
bec80f4f
JS
9962}
9963
5d7836c4
JS
9964/*
9965 * Module to initialise and clean up handlers
9966 */
9967
9968class wxRichTextModule: public wxModule
9969{
9970DECLARE_DYNAMIC_CLASS(wxRichTextModule)
9971public:
9972 wxRichTextModule() {}
cfa3b256
JS
9973 bool OnInit()
9974 {
d2d0adc7 9975 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
9976 wxRichTextBuffer::InitStandardHandlers();
9977 wxRichTextParagraph::InitDefaultTabs();
1aca9fcd
JS
9978
9979 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
9980 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
9981 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
9982 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
9983 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
9984 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
9985 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
9986 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
9987 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
9988
cfa3b256 9989 return true;
47b378bd 9990 }
cfa3b256
JS
9991 void OnExit()
9992 {
9993 wxRichTextBuffer::CleanUpHandlers();
8db2e3ef 9994 wxRichTextBuffer::CleanUpDrawingHandlers();
7c9fdebe 9995 wxRichTextBuffer::CleanUpFieldTypes();
1aca9fcd 9996 wxRichTextXMLHandler::ClearNodeToClassMap();
cfa3b256
JS
9997 wxRichTextDecimalToRoman(-1);
9998 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 9999 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 10000 wxRichTextBuffer::SetRenderer(NULL);
47b378bd 10001 }
5d7836c4
JS
10002};
10003
10004IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10005
10006
f1d6804f
RD
10007// If the richtext lib is dynamically loaded after the app has already started
10008// (such as from wxPython) then the built-in module system will not init this
10009// module. Provide this function to do it manually.
10010void wxRichTextModuleInit()
10011{
10012 wxModule* module = new wxRichTextModule;
10013 module->Init();
10014 wxModule::RegisterModule(module);
10015}
10016
10017
5d7836c4
JS
10018/*!
10019 * Commands for undo/redo
10020 *
10021 */
10022
10023wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
603f702b 10024 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4 10025{
603f702b 10026 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
5d7836c4
JS
10027}
10028
7fe8059f 10029wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
10030{
10031}
10032
10033wxRichTextCommand::~wxRichTextCommand()
10034{
10035 ClearActions();
10036}
10037
10038void wxRichTextCommand::AddAction(wxRichTextAction* action)
10039{
10040 if (!m_actions.Member(action))
10041 m_actions.Append(action);
10042}
10043
10044bool wxRichTextCommand::Do()
10045{
09f14108 10046 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
10047 {
10048 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10049 action->Do();
10050 }
10051
10052 return true;
10053}
10054
10055bool wxRichTextCommand::Undo()
10056{
09f14108 10057 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
10058 {
10059 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10060 action->Undo();
10061 }
10062
10063 return true;
10064}
10065
10066void wxRichTextCommand::ClearActions()
10067{
10068 WX_CLEAR_LIST(wxList, m_actions);
10069}
10070
10071/*!
10072 * Individual action
10073 *
10074 */
10075
603f702b
JS
10076wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10077 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10078 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5d7836c4
JS
10079{
10080 m_buffer = buffer;
603f702b
JS
10081 m_object = NULL;
10082 m_containerAddress.Create(buffer, container);
5d7836c4
JS
10083 m_ignoreThis = ignoreFirstTime;
10084 m_cmdId = id;
10085 m_position = -1;
10086 m_ctrl = ctrl;
10087 m_name = name;
10088 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10089 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10090 if (cmd)
10091 cmd->AddAction(this);
10092}
10093
10094wxRichTextAction::~wxRichTextAction()
10095{
603f702b
JS
10096 if (m_object)
10097 delete m_object;
10098}
10099
10100// Returns the container that this action refers to, using the container address and top-level buffer.
10101wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10102{
10103 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10104 return container;
5d7836c4
JS
10105}
10106
603f702b 10107
7051fa41
JS
10108void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10109{
10110 // Store a list of line start character and y positions so we can figure out which area
10111 // we need to refresh
10112
10113#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
603f702b
JS
10114 wxRichTextParagraphLayoutBox* container = GetContainer();
10115 wxASSERT(container != NULL);
10116 if (!container)
10117 return;
10118
7051fa41
JS
10119 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10120 // If we had several actions, which only invalidate and leave layout until the
10121 // paint handler is called, then this might not be true. So we may need to switch
10122 // optimisation on only when we're simply adding text and not simultaneously
10123 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10124 // first, but of course this means we'll be doing it twice.
603f702b 10125 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
7051fa41 10126 {
4ba36292
JS
10127 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10128 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
7051fa41
JS
10129 int lastY = firstVisiblePt.y + clientSize.y;
10130
603f702b
JS
10131 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10132 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
7051fa41
JS
10133 while (node)
10134 {
10135 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10136 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10137 while (node2)
10138 {
10139 wxRichTextLine* line = node2->GetData();
10140 wxPoint pt = line->GetAbsolutePosition();
10141 wxRichTextRange range = line->GetAbsoluteRange();
10142
10143 if (pt.y > lastY)
10144 {
10145 node2 = wxRichTextLineList::compatibility_iterator();
10146 node = wxRichTextObjectList::compatibility_iterator();
10147 }
10148 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10149 {
10150 optimizationLineCharPositions.Add(range.GetStart());
10151 optimizationLineYPositions.Add(pt.y);
10152 }
10153
10154 if (node2)
10155 node2 = node2->GetNext();
10156 }
10157
10158 if (node)
10159 node = node->GetNext();
10160 }
10161 }
10162#endif
10163}
10164
5d7836c4
JS
10165bool wxRichTextAction::Do()
10166{
10167 m_buffer->Modify(true);
10168
603f702b
JS
10169 wxRichTextParagraphLayoutBox* container = GetContainer();
10170 wxASSERT(container != NULL);
10171 if (!container)
10172 return false;
10173
5d7836c4
JS
10174 switch (m_cmdId)
10175 {
10176 case wxRICHTEXT_INSERT:
10177 {
ea160b2e
JS
10178 // Store a list of line start character and y positions so we can figure out which area
10179 // we need to refresh
10180 wxArrayInt optimizationLineCharPositions;
10181 wxArrayInt optimizationLineYPositions;
10182
10183#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
7051fa41 10184 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
ea160b2e
JS
10185#endif
10186
603f702b
JS
10187 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10188 container->UpdateRanges();
10189
10190 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10191 // Layout() would stop prematurely at the top level.
10192 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
5d7836c4 10193
603f702b 10194 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
0ca07313
JS
10195
10196 // Character position to caret position
10197 newCaretPosition --;
10198
10199 // Don't take into account the last newline
5d7836c4
JS
10200 if (m_newParagraphs.GetPartialParagraph())
10201 newCaretPosition --;
46ee0e5b 10202 else
7c081bd2 10203 if (m_newParagraphs.GetChildren().GetCount() > 1)
46ee0e5b
JS
10204 {
10205 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10206 if (p->GetRange().GetLength() == 1)
10207 newCaretPosition --;
10208 }
5d7836c4 10209
603f702b 10210 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
0ca07313 10211
7051fa41 10212 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
3e541562 10213
5912d19e
JS
10214 wxRichTextEvent cmdEvent(
10215 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10216 m_ctrl ? m_ctrl->GetId() : -1);
10217 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10218 cmdEvent.SetRange(GetRange());
10219 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10220 cmdEvent.SetContainer(container);
3e541562 10221
5912d19e 10222 m_buffer->SendEvent(cmdEvent);
5d7836c4
JS
10223
10224 break;
10225 }
10226 case wxRICHTEXT_DELETE:
10227 {
7051fa41
JS
10228 wxArrayInt optimizationLineCharPositions;
10229 wxArrayInt optimizationLineYPositions;
10230
10231#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10232 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10233#endif
10234
603f702b
JS
10235 container->DeleteRange(GetRange());
10236 container->UpdateRanges();
10237 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10238 // Layout() would stop prematurely at the top level.
10239 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4 10240
6ccbca24 10241 long caretPos = GetRange().GetStart()-1;
603f702b 10242 if (caretPos >= container->GetOwnRange().GetEnd())
6ccbca24
JS
10243 caretPos --;
10244
7051fa41 10245 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
5d7836c4 10246
5912d19e
JS
10247 wxRichTextEvent cmdEvent(
10248 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10249 m_ctrl ? m_ctrl->GetId() : -1);
10250 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10251 cmdEvent.SetRange(GetRange());
10252 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10253 cmdEvent.SetContainer(container);
3e541562 10254
5912d19e
JS
10255 m_buffer->SendEvent(cmdEvent);
10256
5d7836c4
JS
10257 break;
10258 }
10259 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10260 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10261 {
10262 ApplyParagraphs(GetNewParagraphs());
603f702b
JS
10263
10264 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10265 // Layout() would stop prematurely at the top level.
10266 container->InvalidateHierarchy(GetRange());
10267
10268 UpdateAppearance(GetPosition());
10269
10270 wxRichTextEvent cmdEvent(
590a0f8b 10271 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
603f702b
JS
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());
10276 cmdEvent.SetContainer(container);
10277
10278 m_buffer->SendEvent(cmdEvent);
10279
10280 break;
10281 }
10282 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10283 {
10284 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10285 if (obj)
10286 {
10287 wxRichTextAttr oldAttr = obj->GetAttributes();
10288 obj->GetAttributes() = m_attributes;
10289 m_attributes = oldAttr;
10290 }
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());
5d7836c4
JS
10295
10296 UpdateAppearance(GetPosition());
10297
5912d19e
JS
10298 wxRichTextEvent cmdEvent(
10299 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10300 m_ctrl ? m_ctrl->GetId() : -1);
10301 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10302 cmdEvent.SetRange(GetRange());
10303 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10304 cmdEvent.SetContainer(container);
3e541562 10305
5912d19e
JS
10306 m_buffer->SendEvent(cmdEvent);
10307
603f702b
JS
10308 break;
10309 }
10310 case wxRICHTEXT_CHANGE_OBJECT:
10311 {
10312 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10313 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10314 if (obj && m_object)
10315 {
10316 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10317 if (node)
10318 {
10319 wxRichTextObject* obj = node->GetData();
10320 node->SetData(m_object);
10321 m_object = obj;
10322 }
10323 }
10324
10325 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10326 // Layout() would stop prematurely at the top level.
10327 container->InvalidateHierarchy(GetRange());
10328
10329 UpdateAppearance(GetPosition());
10330
10331 // TODO: send new kind of modification event
10332
5d7836c4
JS
10333 break;
10334 }
10335 default:
10336 break;
10337 }
10338
10339 return true;
10340}
10341
10342bool wxRichTextAction::Undo()
10343{
10344 m_buffer->Modify(true);
10345
603f702b
JS
10346 wxRichTextParagraphLayoutBox* container = GetContainer();
10347 wxASSERT(container != NULL);
10348 if (!container)
10349 return false;
10350
5d7836c4
JS
10351 switch (m_cmdId)
10352 {
10353 case wxRICHTEXT_INSERT:
10354 {
7051fa41
JS
10355 wxArrayInt optimizationLineCharPositions;
10356 wxArrayInt optimizationLineYPositions;
10357
10358#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10359 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10360#endif
10361
603f702b
JS
10362 container->DeleteRange(GetRange());
10363 container->UpdateRanges();
7c9fdebe 10364
603f702b
JS
10365 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10366 // Layout() would stop prematurely at the top level.
10367 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
10368
10369 long newCaretPosition = GetPosition() - 1;
3e541562 10370
7051fa41 10371 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10372
5912d19e
JS
10373 wxRichTextEvent cmdEvent(
10374 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10375 m_ctrl ? m_ctrl->GetId() : -1);
10376 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10377 cmdEvent.SetRange(GetRange());
10378 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10379 cmdEvent.SetContainer(container);
3e541562 10380
5912d19e
JS
10381 m_buffer->SendEvent(cmdEvent);
10382
5d7836c4
JS
10383 break;
10384 }
10385 case wxRICHTEXT_DELETE:
10386 {
7051fa41
JS
10387 wxArrayInt optimizationLineCharPositions;
10388 wxArrayInt optimizationLineYPositions;
10389
10390#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10391 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10392#endif
10393
603f702b
JS
10394 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10395 container->UpdateRanges();
7c9fdebe 10396
603f702b
JS
10397 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10398 // Layout() would stop prematurely at the top level.
10399 container->InvalidateHierarchy(GetRange());
5d7836c4 10400
7051fa41 10401 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
5d7836c4 10402
5912d19e
JS
10403 wxRichTextEvent cmdEvent(
10404 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10405 m_ctrl ? m_ctrl->GetId() : -1);
10406 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10407 cmdEvent.SetRange(GetRange());
10408 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10409 cmdEvent.SetContainer(container);
3e541562 10410
5912d19e
JS
10411 m_buffer->SendEvent(cmdEvent);
10412
5d7836c4
JS
10413 break;
10414 }
10415 case wxRICHTEXT_CHANGE_STYLE:
590a0f8b 10416 case wxRICHTEXT_CHANGE_PROPERTIES:
5d7836c4
JS
10417 {
10418 ApplyParagraphs(GetOldParagraphs());
603f702b
JS
10419 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10420 // Layout() would stop prematurely at the top level.
10421 container->InvalidateHierarchy(GetRange());
5d7836c4
JS
10422
10423 UpdateAppearance(GetPosition());
10424
5912d19e 10425 wxRichTextEvent cmdEvent(
590a0f8b 10426 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
5912d19e
JS
10427 m_ctrl ? m_ctrl->GetId() : -1);
10428 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10429 cmdEvent.SetRange(GetRange());
10430 cmdEvent.SetPosition(GetRange().GetStart());
603f702b 10431 cmdEvent.SetContainer(container);
3e541562 10432
5912d19e
JS
10433 m_buffer->SendEvent(cmdEvent);
10434
5d7836c4
JS
10435 break;
10436 }
603f702b
JS
10437 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10438 case wxRICHTEXT_CHANGE_OBJECT:
10439 {
10440 return Do();
10441 }
5d7836c4
JS
10442 default:
10443 break;
10444 }
10445
10446 return true;
10447}
10448
10449/// Update the control appearance
603f702b 10450void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
5d7836c4 10451{
603f702b
JS
10452 wxRichTextParagraphLayoutBox* container = GetContainer();
10453 wxASSERT(container != NULL);
10454 if (!container)
10455 return;
10456
5d7836c4
JS
10457 if (m_ctrl)
10458 {
603f702b 10459 m_ctrl->SetFocusObject(container);
5d7836c4 10460 m_ctrl->SetCaretPosition(caretPosition);
603f702b 10461
5d7836c4
JS
10462 if (!m_ctrl->IsFrozen())
10463 {
603f702b
JS
10464 wxRect containerRect = container->GetRect();
10465
2f36e8dc 10466 m_ctrl->LayoutContent();
5d7836c4 10467
603f702b
JS
10468 // Refresh everything if there were floating objects or the container changed size
10469 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10470 if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
10471 {
10472 m_ctrl->Refresh(false);
10473 }
10474 else
10475
10476#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10477 // Find refresh rectangle if we are in a position to optimise refresh
10478 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10479 {
10480 size_t i;
10481
4ba36292
JS
10482 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10483 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
603f702b
JS
10484
10485 // Start/end positions
10486 int firstY = 0;
10487 int lastY = firstVisiblePt.y + clientSize.y;
10488
10489 bool foundEnd = false;
10490
10491 // position offset - how many characters were inserted
10492 int positionOffset = GetRange().GetLength();
10493
10494 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10495 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10496 positionOffset = - positionOffset;
10497
10498 // find the first line which is being drawn at the same position as it was
10499 // before. Since we're talking about a simple insertion, we can assume
10500 // that the rest of the window does not need to be redrawn.
10501
10502 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10503 // Since we support floating layout, we should redraw the whole para instead of just
10504 // the first line touching the invalid range.
10505 if (para)
10506 {
10507 firstY = para->GetPosition().y;
10508 }
10509
10510 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10511 while (node)
10512 {
10513 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10514 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10515 while (node2)
10516 {
10517 wxRichTextLine* line = node2->GetData();
10518 wxPoint pt = line->GetAbsolutePosition();
10519 wxRichTextRange range = line->GetAbsoluteRange();
10520
10521 // we want to find the first line that is in the same position
10522 // as before. This will mean we're at the end of the changed text.
10523
10524 if (pt.y > lastY) // going past the end of the window, no more info
10525 {
10526 node2 = wxRichTextLineList::compatibility_iterator();
10527 node = wxRichTextObjectList::compatibility_iterator();
10528 }
10529 // Detect last line in the buffer
10530 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10531 {
10532 // If deleting text, make sure we refresh below as well as above
10533 if (positionOffset >= 0)
10534 {
10535 foundEnd = true;
10536 lastY = pt.y + line->GetSize().y;
10537 }
10538
10539 node2 = wxRichTextLineList::compatibility_iterator();
10540 node = wxRichTextObjectList::compatibility_iterator();
10541
10542 break;
10543 }
10544 else
10545 {
10546 // search for this line being at the same position as before
10547 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10548 {
10549 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10550 ((*optimizationLineYPositions)[i] == pt.y))
10551 {
10552 // Stop, we're now the same as we were
10553 foundEnd = true;
10554
10555 lastY = pt.y;
10556
10557 node2 = wxRichTextLineList::compatibility_iterator();
10558 node = wxRichTextObjectList::compatibility_iterator();
10559
10560 break;
10561 }
10562 }
10563 }
10564
10565 if (node2)
10566 node2 = node2->GetNext();
10567 }
10568
10569 if (node)
10570 node = node->GetNext();
10571 }
10572
10573 firstY = wxMax(firstVisiblePt.y, firstY);
10574 if (!foundEnd)
10575 lastY = firstVisiblePt.y + clientSize.y;
10576
10577 // Convert to device coordinates
4ba36292 10578 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
603f702b
JS
10579 m_ctrl->RefreshRect(rect);
10580 }
10581 else
1c13f06e 10582#endif
603f702b
JS
10583 m_ctrl->Refresh(false);
10584
10585 m_ctrl->PositionCaret();
4fe83b93
JS
10586
10587 // This causes styles to persist when doing programmatic
10588 // content creation except when Freeze/Thaw is used, so
10589 // disable this and check for the consequences.
10590 // m_ctrl->SetDefaultStyleToCursorStyle();
603f702b 10591
5d7836c4 10592 if (sendUpdateEvent)
0ec1179b 10593 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
5d7836c4 10594 }
7fe8059f 10595 }
5d7836c4
JS
10596}
10597
10598/// Replace the buffer paragraphs with the new ones.
0ca07313 10599void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4 10600{
603f702b
JS
10601 wxRichTextParagraphLayoutBox* container = GetContainer();
10602 wxASSERT(container != NULL);
10603 if (!container)
10604 return;
10605
5d7836c4
JS
10606 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
10607 while (node)
10608 {
10609 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
10610 wxASSERT (para != NULL);
10611
10612 // We'll replace the existing paragraph by finding the paragraph at this position,
10613 // delete its node data, and setting a copy as the new node data.
10614 // TODO: make more efficient by simply swapping old and new paragraph objects.
10615
603f702b 10616 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
5d7836c4
JS
10617 if (existingPara)
10618 {
603f702b 10619 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
5d7836c4
JS
10620 if (bufferParaNode)
10621 {
10622 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
603f702b 10623 newPara->SetParent(container);
5d7836c4
JS
10624
10625 bufferParaNode->SetData(newPara);
10626
10627 delete existingPara;
10628 }
10629 }
10630
10631 node = node->GetNext();
10632 }
10633}
10634
10635
10636/*!
10637 * wxRichTextRange
10638 * This stores beginning and end positions for a range of data.
10639 */
10640
603f702b
JS
10641WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
10642
5d7836c4
JS
10643/// Limit this range to be within 'range'
10644bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
10645{
10646 if (m_start < range.m_start)
10647 m_start = range.m_start;
10648
10649 if (m_end > range.m_end)
10650 m_end = range.m_end;
10651
10652 return true;
10653}
10654
10655/*!
10656 * wxRichTextImage implementation
10657 * This object represents an image.
10658 */
10659
bec80f4f 10660IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
5d7836c4 10661
24777478 10662wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 10663 wxRichTextObject(parent)
5d7836c4 10664{
23698b12 10665 Init();
cdaed652 10666 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
4f32b3cf
JS
10667 if (charStyle)
10668 SetAttributes(*charStyle);
5d7836c4
JS
10669}
10670
24777478 10671wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
bec80f4f 10672 wxRichTextObject(parent)
5d7836c4 10673{
23698b12 10674 Init();
5d7836c4 10675 m_imageBlock = imageBlock;
4f32b3cf
JS
10676 if (charStyle)
10677 SetAttributes(*charStyle);
5d7836c4
JS
10678}
10679
23698b12
JS
10680void wxRichTextImage::Init()
10681{
10682 m_originalImageSize = wxSize(-1, -1);
10683}
10684
cdaed652
VZ
10685/// Create a cached image at the required size
10686bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
5d7836c4 10687{
23698b12
JS
10688 if (!m_imageBlock.IsOk())
10689 return false;
10690
10691 // If we have an original image size, use that to compute the cached bitmap size
10692 // instead of loading the image each time. This way we can avoid loading
10693 // the image so long as the new cached bitmap size hasn't changed.
10694
10695 wxImage image;
10696 if (resetCache || m_originalImageSize == wxSize(-1, -1))
cdaed652 10697 {
23698b12 10698 m_imageCache = wxNullBitmap;
ce00f59b 10699
cdaed652
VZ
10700 m_imageBlock.Load(image);
10701 if (!image.IsOk())
10702 return false;
ce00f59b 10703
23698b12
JS
10704 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
10705 }
10706
10707 int width = m_originalImageSize.GetWidth();
10708 int height = m_originalImageSize.GetHeight();
10709
10710 int parentWidth = 0;
10711 int parentHeight = 0;
bec80f4f 10712
23698b12
JS
10713 int maxWidth = -1;
10714 int maxHeight = -1;
10715
10716 wxRichTextBuffer* buffer = GetBuffer();
10717 if (buffer)
10718 {
10719 wxSize sz;
10720 if (buffer->GetRichTextCtrl())
cdaed652 10721 {
23698b12
JS
10722 // Subtract borders
10723 sz = buffer->GetRichTextCtrl()->GetClientSize();
10724
10725 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10726 marginRect = wxRect(0, 0, sz.x, sz.y);
10727 buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
10728
10729 sz = contentRect.GetSize();
10730
10731 // Start with a maximum width of the control size, even if not specified by the content,
10732 // to minimize the amount of picture overlapping the right-hand side
10733 maxWidth = sz.x;
cdaed652 10734 }
23698b12
JS
10735 else
10736 sz = buffer->GetCachedSize();
10737 parentWidth = sz.GetWidth();
10738 parentHeight = sz.GetHeight();
10739 }
10740
10741 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
10742 {
10743 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10744 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
10745 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10746 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
10747 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10748 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
10749 }
10750
10751 // Limit to max width
10752
10753 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
10754 {
10755 int mw = -1;
10756
10757 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10758 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
10759 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10760 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
10761 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10762 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
10763
10764 // If we already have a smaller max width due to the constraints of the control size,
10765 // don't use the larger max width.
10766 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
10767 maxWidth = mw;
10768 }
10769
10770 if (maxWidth > 0 && width > maxWidth)
10771 width = maxWidth;
10772
10773 // Preserve the aspect ratio
10774 if (width != m_originalImageSize.GetWidth())
10775 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
10776
10777 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
10778 {
10779 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10780 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
10781 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10782 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
10783 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10784 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
10785
10786 // Preserve the aspect ratio
10787 if (height != m_originalImageSize.GetHeight())
10788 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
10789 }
10790
10791 // Limit to max height
10792
10793 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
10794 {
10795 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
10796 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
10797 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
10798 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
10799 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
10800 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
10801 }
10802
10803 if (maxHeight > 0 && height > maxHeight)
10804 {
10805 height = maxHeight;
10806
10807 // Preserve the aspect ratio
10808 if (height != m_originalImageSize.GetHeight())
10809 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
10810 }
10811
10812 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
10813 {
10814 // Do nothing, we didn't need to change the image cache
10815 }
10816 else
10817 {
10818 if (!image.IsOk())
cdaed652 10819 {
23698b12
JS
10820 m_imageBlock.Load(image);
10821 if (!image.IsOk())
10822 return false;
cdaed652 10823 }
5d7836c4 10824
cdaed652
VZ
10825 if (image.GetWidth() == width && image.GetHeight() == height)
10826 m_imageCache = wxBitmap(image);
10827 else
10828 {
10829 // If the original width and height is small, e.g. 400 or below,
10830 // scale up and then down to improve image quality. This can make
10831 // a big difference, with not much performance hit.
10832 int upscaleThreshold = 400;
10833 wxImage img;
10834 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
10835 {
10836 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
10837 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
10838 }
10839 else
10840 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
10841 m_imageCache = wxBitmap(img);
10842 }
10843 }
ce00f59b 10844
cdaed652 10845 return m_imageCache.IsOk();
5d7836c4
JS
10846}
10847
5d7836c4 10848/// Draw the item
20d09da5 10849bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
5d7836c4 10850{
603f702b
JS
10851 if (!IsShown())
10852 return true;
10853
cdaed652
VZ
10854 // Don't need cached size AFAIK
10855 // wxSize size = GetCachedSize();
10856 if (!LoadImageCache(dc))
5d7836c4 10857 return false;
ce00f59b 10858
8db2e3ef
JS
10859 wxRichTextAttr attr(GetAttributes());
10860 context.ApplyVirtualAttributes(attr, this);
10861
10862 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
603f702b 10863
603f702b
JS
10864 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10865 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10866 marginRect = rect; // outer rectangle, will calculate contentRect
8db2e3ef 10867 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10868
10869 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
5d7836c4 10870
a70eb13e 10871 if (selection.WithinSelection(GetRange().GetStart(), this))
5d7836c4 10872 {
ecb5fbf1
JS
10873 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
10874 wxCheckSetPen(dc, *wxBLACK_PEN);
5d7836c4 10875 dc.SetLogicalFunction(wxINVERT);
603f702b 10876 dc.DrawRectangle(contentRect);
5d7836c4
JS
10877 dc.SetLogicalFunction(wxCOPY);
10878 }
10879
10880 return true;
10881}
10882
10883/// Lay the item out
8db2e3ef 10884bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
5d7836c4 10885{
cdaed652
VZ
10886 if (!LoadImageCache(dc))
10887 return false;
5d7836c4 10888
603f702b
JS
10889 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
10890 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10891 contentRect = wxRect(wxPoint(0,0), imageSize);
8db2e3ef
JS
10892
10893 wxRichTextAttr attr(GetAttributes());
10894 context.ApplyVirtualAttributes(attr, this);
10895
10896 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10897
10898 wxSize overallSize = marginRect.GetSize();
10899
10900 SetCachedSize(overallSize);
10901 SetMaxSize(overallSize);
10902 SetMinSize(overallSize);
cdaed652 10903 SetPosition(rect.GetPosition());
5d7836c4
JS
10904
10905 return true;
10906}
10907
10908/// Get/set the object size for the given range. Returns false if the range
10909/// is invalid for this object.
8db2e3ef 10910bool 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
10911{
10912 if (!range.IsWithin(GetRange()))
10913 return false;
10914
cdaed652 10915 if (!((wxRichTextImage*)this)->LoadImageCache(dc))
31778480 10916 {
cdaed652
VZ
10917 size.x = 0; size.y = 0;
10918 if (partialExtents)
31778480 10919 partialExtents->Add(0);
cdaed652 10920 return false;
31778480 10921 }
ce00f59b 10922
8db2e3ef
JS
10923 wxRichTextAttr attr(GetAttributes());
10924 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
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 10929 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
603f702b
JS
10930
10931 wxSize overallSize = marginRect.GetSize();
31778480 10932
cdaed652 10933 if (partialExtents)
603f702b 10934 partialExtents->Add(overallSize.x);
5d7836c4 10935
603f702b 10936 size = overallSize;
5d7836c4
JS
10937
10938 return true;
10939}
10940
603f702b
JS
10941// Get the 'natural' size for an object. For an image, it would be the
10942// image size.
10943wxTextAttrSize wxRichTextImage::GetNaturalSize() const
10944{
10945 wxTextAttrSize size;
10946 if (GetImageCache().IsOk())
10947 {
10948 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
10949 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
10950 }
10951 return size;
10952}
10953
10954
5d7836c4
JS
10955/// Copy
10956void wxRichTextImage::Copy(const wxRichTextImage& obj)
10957{
bec80f4f 10958 wxRichTextObject::Copy(obj);
59509217 10959
5d7836c4 10960 m_imageBlock = obj.m_imageBlock;
23698b12 10961 m_originalImageSize = obj.m_originalImageSize;
5d7836c4
JS
10962}
10963
cdaed652
VZ
10964/// Edit properties via a GUI
10965bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10966{
603f702b
JS
10967 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
10968 imageDlg.SetAttributes(GetAttributes());
cdaed652
VZ
10969
10970 if (imageDlg.ShowModal() == wxID_OK)
10971 {
603f702b
JS
10972 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10973 // indeterminate in the object.
10974 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
cdaed652
VZ
10975 return true;
10976 }
10977 else
10978 return false;
10979}
10980
5d7836c4
JS
10981/*!
10982 * Utilities
10983 *
10984 */
10985
10986/// Compare two attribute objects
24777478 10987bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
5d7836c4 10988{
38f833b1 10989 return (attr1 == attr2);
5d7836c4
JS
10990}
10991
44cc96a8
JS
10992/// Compare tabs
10993bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
10994{
10995 if (tabs1.GetCount() != tabs2.GetCount())
5d7836c4
JS
10996 return false;
10997
44cc96a8
JS
10998 size_t i;
10999 for (i = 0; i < tabs1.GetCount(); i++)
11000 {
11001 if (tabs1[i] != tabs2[i])
11002 return false;
11003 }
11004 return true;
11005}
5d7836c4 11006
24777478 11007bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
44cc96a8
JS
11008{
11009 return destStyle.Apply(style, compareWith);
11010}
5d7836c4 11011
44cc96a8 11012// Remove attributes
24777478 11013bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
44cc96a8 11014{
24777478 11015 return destStyle.RemoveStyle(style);
44cc96a8 11016}
5d7836c4 11017
44cc96a8
JS
11018/// Combine two bitlists, specifying the bits of interest with separate flags.
11019bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11020{
24777478 11021 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
44cc96a8 11022}
5d7836c4 11023
44cc96a8
JS
11024/// Compare two bitlists
11025bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11026{
24777478 11027 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
44cc96a8 11028}
38f833b1 11029
44cc96a8 11030/// Split into paragraph and character styles
24777478 11031bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
44cc96a8 11032{
24777478 11033 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
44cc96a8 11034}
5d7836c4 11035
44cc96a8
JS
11036/// Convert a decimal to Roman numerals
11037wxString wxRichTextDecimalToRoman(long n)
11038{
11039 static wxArrayInt decimalNumbers;
11040 static wxArrayString romanNumbers;
5d7836c4 11041
44cc96a8
JS
11042 // Clean up arrays
11043 if (n == -1)
11044 {
11045 decimalNumbers.Clear();
11046 romanNumbers.Clear();
11047 return wxEmptyString;
11048 }
5d7836c4 11049
44cc96a8
JS
11050 if (decimalNumbers.GetCount() == 0)
11051 {
11052 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
59509217 11053
44cc96a8
JS
11054 wxRichTextAddDecRom(1000, wxT("M"));
11055 wxRichTextAddDecRom(900, wxT("CM"));
11056 wxRichTextAddDecRom(500, wxT("D"));
11057 wxRichTextAddDecRom(400, wxT("CD"));
11058 wxRichTextAddDecRom(100, wxT("C"));
11059 wxRichTextAddDecRom(90, wxT("XC"));
11060 wxRichTextAddDecRom(50, wxT("L"));
11061 wxRichTextAddDecRom(40, wxT("XL"));
11062 wxRichTextAddDecRom(10, wxT("X"));
11063 wxRichTextAddDecRom(9, wxT("IX"));
11064 wxRichTextAddDecRom(5, wxT("V"));
11065 wxRichTextAddDecRom(4, wxT("IV"));
11066 wxRichTextAddDecRom(1, wxT("I"));
11067 }
5d7836c4 11068
44cc96a8
JS
11069 int i = 0;
11070 wxString roman;
ea160b2e 11071
44cc96a8 11072 while (n > 0 && i < 13)
42688aea 11073 {
44cc96a8
JS
11074 if (n >= decimalNumbers[i])
11075 {
11076 n -= decimalNumbers[i];
11077 roman += romanNumbers[i];
11078 }
11079 else
11080 {
11081 i ++;
11082 }
42688aea 11083 }
44cc96a8
JS
11084 if (roman.IsEmpty())
11085 roman = wxT("0");
11086 return roman;
11087}
42688aea 11088
44cc96a8
JS
11089/*!
11090 * wxRichTextFileHandler
11091 * Base class for file handlers
11092 */
4d6d8bf4 11093
44cc96a8 11094IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
5d7836c4 11095
44cc96a8
JS
11096#if wxUSE_FFILE && wxUSE_STREAMS
11097bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
5d7836c4 11098{
44cc96a8 11099 wxFFileInputStream stream(filename);
a1b806b9 11100 if (stream.IsOk())
44cc96a8 11101 return LoadFile(buffer, stream);
5d7836c4 11102
44cc96a8
JS
11103 return false;
11104}
5d7836c4 11105
44cc96a8
JS
11106bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11107{
11108 wxFFileOutputStream stream(filename);
a1b806b9 11109 if (stream.IsOk())
44cc96a8 11110 return SaveFile(buffer, stream);
5d7836c4 11111
44cc96a8
JS
11112 return false;
11113}
11114#endif // wxUSE_FFILE && wxUSE_STREAMS
5d7836c4 11115
44cc96a8
JS
11116/// Can we handle this filename (if using files)? By default, checks the extension.
11117bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11118{
11119 wxString path, file, ext;
a51e601e 11120 wxFileName::SplitPath(filename, & path, & file, & ext);
5d7836c4 11121
44cc96a8
JS
11122 return (ext.Lower() == GetExtension());
11123}
5d7836c4 11124
44cc96a8
JS
11125/*!
11126 * wxRichTextTextHandler
11127 * Plain text handler
11128 */
5d7836c4 11129
44cc96a8 11130IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
5d7836c4 11131
44cc96a8
JS
11132#if wxUSE_STREAMS
11133bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11134{
11135 if (!stream.IsOk())
797e38dd
JS
11136 return false;
11137
44cc96a8
JS
11138 wxString str;
11139 int lastCh = 0;
5d7836c4 11140
44cc96a8
JS
11141 while (!stream.Eof())
11142 {
11143 int ch = stream.GetC();
5d7836c4 11144
44cc96a8
JS
11145 if (!stream.Eof())
11146 {
11147 if (ch == 10 && lastCh != 13)
11148 str += wxT('\n');
5d7836c4 11149
44cc96a8
JS
11150 if (ch > 0 && ch != 10)
11151 str += wxChar(ch);
5d7836c4 11152
44cc96a8
JS
11153 lastCh = ch;
11154 }
11155 }
5d7836c4 11156
44cc96a8
JS
11157 buffer->ResetAndClearCommands();
11158 buffer->Clear();
11159 buffer->AddParagraphs(str);
11160 buffer->UpdateRanges();
5d7836c4 11161
44cc96a8
JS
11162 return true;
11163}
5d7836c4 11164
44cc96a8
JS
11165bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11166{
11167 if (!stream.IsOk())
5d7836c4
JS
11168 return false;
11169
44cc96a8 11170 wxString text = buffer->GetText();
38f833b1 11171
44cc96a8
JS
11172 wxString newLine = wxRichTextLineBreakChar;
11173 text.Replace(newLine, wxT("\n"));
5d7836c4 11174
44cc96a8 11175 wxCharBuffer buf = text.ToAscii();
5d7836c4 11176
44cc96a8
JS
11177 stream.Write((const char*) buf, text.length());
11178 return true;
11179}
11180#endif // wxUSE_STREAMS
5d7836c4 11181
44cc96a8
JS
11182/*
11183 * Stores information about an image, in binary in-memory form
11184 */
59509217 11185
44cc96a8
JS
11186wxRichTextImageBlock::wxRichTextImageBlock()
11187{
11188 Init();
11189}
5d7836c4 11190
44cc96a8
JS
11191wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11192{
11193 Init();
11194 Copy(block);
11195}
ea160b2e 11196
44cc96a8
JS
11197wxRichTextImageBlock::~wxRichTextImageBlock()
11198{
5276b0a5 11199 wxDELETEA(m_data);
5d7836c4
JS
11200}
11201
44cc96a8 11202void wxRichTextImageBlock::Init()
5d7836c4
JS
11203{
11204 m_data = NULL;
11205 m_dataSize = 0;
d75a69e8 11206 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11207}
11208
11209void wxRichTextImageBlock::Clear()
11210{
5276b0a5 11211 wxDELETEA(m_data);
5d7836c4 11212 m_dataSize = 0;
d75a69e8 11213 m_imageType = wxBITMAP_TYPE_INVALID;
5d7836c4
JS
11214}
11215
11216
11217// Load the original image into a memory block.
11218// If the image is not a JPEG, we must convert it into a JPEG
11219// to conserve space.
11220// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11221// load the image a 2nd time.
11222
d75a69e8
FM
11223bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11224 wxImage& image, bool convertToJPEG)
5d7836c4
JS
11225{
11226 m_imageType = imageType;
11227
11228 wxString filenameToRead(filename);
7fe8059f 11229 bool removeFile = false;
5d7836c4 11230
62891c87 11231 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11232 return false; // Could not determine image type
5d7836c4
JS
11233
11234 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11235 {
a51e601e
FM
11236 wxString tempFile =
11237 wxFileName::CreateTempFileName(_("image"));
5d7836c4 11238
a51e601e 11239 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11240
11241 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11242 filenameToRead = tempFile;
7fe8059f 11243 removeFile = true;
5d7836c4
JS
11244
11245 m_imageType = wxBITMAP_TYPE_JPEG;
11246 }
11247 wxFile file;
11248 if (!file.Open(filenameToRead))
7fe8059f 11249 return false;
5d7836c4
JS
11250
11251 m_dataSize = (size_t) file.Length();
11252 file.Close();
11253
11254 if (m_data)
11255 delete[] m_data;
11256 m_data = ReadBlock(filenameToRead, m_dataSize);
11257
11258 if (removeFile)
11259 wxRemoveFile(filenameToRead);
11260
11261 return (m_data != NULL);
11262}
11263
11264// Make an image block from the wxImage in the given
11265// format.
d75a69e8 11266bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
5d7836c4 11267{
5d7836c4
JS
11268 image.SetOption(wxT("quality"), quality);
11269
62891c87 11270 if (imageType == wxBITMAP_TYPE_INVALID)
7fe8059f 11271 return false; // Could not determine image type
5d7836c4 11272
cdaed652
VZ
11273 return DoMakeImageBlock(image, imageType);
11274}
11275
11276// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11277bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11278{
11279 if (imageType == wxBITMAP_TYPE_INVALID)
11280 return false; // Could not determine image type
ce00f59b 11281
cdaed652
VZ
11282 return DoMakeImageBlock(image, imageType);
11283}
7fe8059f 11284
cdaed652
VZ
11285// Makes the image block
11286bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11287{
11288 wxMemoryOutputStream memStream;
11289 if (!image.SaveFile(memStream, imageType))
5d7836c4 11290 {
7fe8059f 11291 return false;
5d7836c4 11292 }
ce00f59b 11293
cdaed652
VZ
11294 unsigned char* block = new unsigned char[memStream.GetSize()];
11295 if (!block)
377c1ba4 11296 return false;
ce00f59b 11297
5d7836c4
JS
11298 if (m_data)
11299 delete[] m_data;
cdaed652 11300 m_data = block;
ce00f59b
VZ
11301
11302 m_imageType = imageType;
cdaed652 11303 m_dataSize = memStream.GetSize();
5d7836c4 11304
cdaed652 11305 memStream.CopyTo(m_data, m_dataSize);
5d7836c4
JS
11306
11307 return (m_data != NULL);
11308}
11309
5d7836c4
JS
11310// Write to a file
11311bool wxRichTextImageBlock::Write(const wxString& filename)
11312{
11313 return WriteBlock(filename, m_data, m_dataSize);
11314}
11315
11316void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11317{
11318 m_imageType = block.m_imageType;
5276b0a5 11319 wxDELETEA(m_data);
5d7836c4
JS
11320 m_dataSize = block.m_dataSize;
11321 if (m_dataSize == 0)
11322 return;
11323
11324 m_data = new unsigned char[m_dataSize];
11325 unsigned int i;
11326 for (i = 0; i < m_dataSize; i++)
11327 m_data[i] = block.m_data[i];
11328}
11329
11330//// Operators
11331void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11332{
11333 Copy(block);
11334}
11335
11336// Load a wxImage from the block
11337bool wxRichTextImageBlock::Load(wxImage& image)
11338{
11339 if (!m_data)
7fe8059f 11340 return false;
5d7836c4
JS
11341
11342 // Read in the image.
0ca07313 11343#if wxUSE_STREAMS
5d7836c4
JS
11344 wxMemoryInputStream mstream(m_data, m_dataSize);
11345 bool success = image.LoadFile(mstream, GetImageType());
11346#else
a51e601e
FM
11347 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11348 wxASSERT(!tempFile.IsEmpty());
5d7836c4
JS
11349
11350 if (!WriteBlock(tempFile, m_data, m_dataSize))
11351 {
7fe8059f 11352 return false;
5d7836c4
JS
11353 }
11354 success = image.LoadFile(tempFile, GetImageType());
11355 wxRemoveFile(tempFile);
11356#endif
11357
11358 return success;
11359}
11360
11361// Write data in hex to a stream
11362bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11363{
4dc7ae1a
JS
11364 if (m_dataSize == 0)
11365 return true;
11366
11367 int bufSize = 100000;
a3c12576
JS
11368 if (int(2*m_dataSize) < bufSize)
11369 bufSize = 2*m_dataSize;
4dc7ae1a 11370 char* buf = new char[bufSize+1];
351c0647
JS
11371
11372 int left = m_dataSize;
11373 int n, i, j;
11374 j = 0;
11375 while (left > 0)
5d7836c4 11376 {
351c0647
JS
11377 if (left*2 > bufSize)
11378 {
11379 n = bufSize; left -= (bufSize/2);
11380 }
11381 else
11382 {
11383 n = left*2; left = 0;
11384 }
7fe8059f 11385
351c0647
JS
11386 char* b = buf;
11387 for (i = 0; i < (n/2); i++)
11388 {
f728025e 11389 wxDecToHex(m_data[j], b, b+1);
351c0647
JS
11390 b += 2; j ++;
11391 }
5d7836c4 11392
351c0647
JS
11393 buf[n] = 0;
11394 stream.Write((const char*) buf, n);
11395 }
4dc7ae1a 11396 delete[] buf;
5d7836c4
JS
11397 return true;
11398}
11399
11400// Read data in hex from a stream
d75a69e8 11401bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
5d7836c4
JS
11402{
11403 int dataSize = length/2;
11404
11405 if (m_data)
11406 delete[] m_data;
11407
046fce47
FM
11408 // create a null terminated temporary string:
11409 char str[3];
11410 str[2] = '\0';
11411
5d7836c4
JS
11412 m_data = new unsigned char[dataSize];
11413 int i;
11414 for (i = 0; i < dataSize; i ++)
11415 {
c9f78968
VS
11416 str[0] = (char)stream.GetC();
11417 str[1] = (char)stream.GetC();
5d7836c4 11418
a9465653 11419 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
11420 }
11421
11422 m_dataSize = dataSize;
11423 m_imageType = imageType;
11424
11425 return true;
11426}
11427
5d7836c4
JS
11428// Allocate and read from stream as a block of memory
11429unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11430{
11431 unsigned char* block = new unsigned char[size];
11432 if (!block)
11433 return NULL;
11434
11435 stream.Read(block, size);
11436
11437 return block;
11438}
11439
11440unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11441{
11442 wxFileInputStream stream(filename);
a1b806b9 11443 if (!stream.IsOk())
5d7836c4
JS
11444 return NULL;
11445
11446 return ReadBlock(stream, size);
11447}
11448
11449// Write memory block to stream
11450bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11451{
11452 stream.Write((void*) block, size);
11453 return stream.IsOk();
11454
11455}
11456
11457// Write memory block to file
11458bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11459{
11460 wxFileOutputStream outStream(filename);
a1b806b9 11461 if (!outStream.IsOk())
7fe8059f 11462 return false;
5d7836c4
JS
11463
11464 return WriteBlock(outStream, block, size);
11465}
11466
d2d0adc7
JS
11467// Gets the extension for the block's type
11468wxString wxRichTextImageBlock::GetExtension() const
11469{
11470 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11471 if (handler)
11472 return handler->GetExtension();
11473 else
11474 return wxEmptyString;
11475}
11476
0ca07313
JS
11477#if wxUSE_DATAOBJ
11478
11479/*!
11480 * The data object for a wxRichTextBuffer
11481 */
11482
11483const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11484
11485wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11486{
11487 m_richTextBuffer = richTextBuffer;
11488
11489 // this string should uniquely identify our format, but is otherwise
11490 // arbitrary
11491 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11492
11493 SetFormat(m_formatRichTextBuffer);
11494}
11495
11496wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11497{
11498 delete m_richTextBuffer;
11499}
11500
11501// after a call to this function, the richTextBuffer is owned by the caller and it
11502// is responsible for deleting it!
11503wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11504{
11505 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11506 m_richTextBuffer = NULL;
11507
11508 return richTextBuffer;
11509}
11510
11511wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11512{
11513 return m_formatRichTextBuffer;
11514}
11515
11516size_t wxRichTextBufferDataObject::GetDataSize() const
11517{
11518 if (!m_richTextBuffer)
11519 return 0;
11520
11521 wxString bufXML;
11522
11523 {
11524 wxStringOutputStream stream(& bufXML);
11525 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11526 {
11527 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11528 return 0;
11529 }
11530 }
11531
11532#if wxUSE_UNICODE
11533 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11534 return strlen(buffer) + 1;
11535#else
11536 return bufXML.Length()+1;
11537#endif
11538}
11539
11540bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11541{
11542 if (!pBuf || !m_richTextBuffer)
11543 return false;
11544
11545 wxString bufXML;
11546
11547 {
11548 wxStringOutputStream stream(& bufXML);
11549 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11550 {
11551 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11552 return 0;
11553 }
11554 }
11555
11556#if wxUSE_UNICODE
11557 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11558 size_t len = strlen(buffer);
11559 memcpy((char*) pBuf, (const char*) buffer, len);
11560 ((char*) pBuf)[len] = 0;
11561#else
11562 size_t len = bufXML.Length();
11563 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11564 ((char*) pBuf)[len] = 0;
11565#endif
11566
11567 return true;
11568}
11569
11570bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
11571{
5276b0a5 11572 wxDELETE(m_richTextBuffer);
0ca07313
JS
11573
11574 wxString bufXML((const char*) buf, wxConvUTF8);
11575
11576 m_richTextBuffer = new wxRichTextBuffer;
11577
11578 wxStringInputStream stream(bufXML);
11579 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
11580 {
11581 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11582
5276b0a5 11583 wxDELETE(m_richTextBuffer);
0ca07313
JS
11584
11585 return false;
11586 }
11587 return true;
11588}
11589
11590#endif
11591 // wxUSE_DATAOBJ
11592
44cc96a8
JS
11593
11594/*
11595 * wxRichTextFontTable
11596 * Manages quick access to a pool of fonts for rendering rich text
11597 */
11598
d65381ac 11599WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
44cc96a8
JS
11600
11601class wxRichTextFontTableData: public wxObjectRefData
11602{
11603public:
11604 wxRichTextFontTableData() {}
11605
32423dd8 11606 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
44cc96a8
JS
11607
11608 wxRichTextFontTableHashMap m_hashMap;
11609};
11610
32423dd8 11611wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
44cc96a8
JS
11612{
11613 wxString facename(fontSpec.GetFontFaceName());
44cc96a8 11614
32423dd8
JS
11615 int fontSize = fontSpec.GetFontSize();
11616 if (fontScale != 1.0)
11617 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
11618
11619 wxString units;
11620 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
11621 units = wxT("px");
11622 else
11623 units = wxT("pt");
11624 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
11625 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
11626 facename.c_str(), (int) fontSpec.GetFontEncoding());
11627
11628 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
44cc96a8
JS
11629 if ( entry == m_hashMap.end() )
11630 {
32423dd8
JS
11631 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
11632 {
b42e4b3e 11633 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
32423dd8
JS
11634 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
11635 font.SetStrikethrough(true);
11636 m_hashMap[spec] = font;
11637 return font;
11638 }
11639 else
11640 {
5a0e33af 11641 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
32423dd8
JS
11642 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
11643 font.SetStrikethrough(true);
11644
11645 m_hashMap[spec] = font;
11646 return font;
11647 }
44cc96a8
JS
11648 }
11649 else
11650 {
11651 return entry->second;
11652 }
11653}
11654
11655IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
11656
11657wxRichTextFontTable::wxRichTextFontTable()
11658{
11659 m_refData = new wxRichTextFontTableData;
32423dd8 11660 m_fontScale = 1.0;
44cc96a8
JS
11661}
11662
11663wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
2a230426 11664 : wxObject()
44cc96a8
JS
11665{
11666 (*this) = table;
11667}
11668
11669wxRichTextFontTable::~wxRichTextFontTable()
11670{
11671 UnRef();
11672}
11673
11674bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
11675{
11676 return (m_refData == table.m_refData);
11677}
11678
11679void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
11680{
11681 Ref(table);
32423dd8 11682 m_fontScale = table.m_fontScale;
44cc96a8
JS
11683}
11684
24777478 11685wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
44cc96a8
JS
11686{
11687 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
11688 if (data)
32423dd8 11689 return data->FindFont(fontSpec, m_fontScale);
44cc96a8
JS
11690 else
11691 return wxFont();
11692}
11693
11694void wxRichTextFontTable::Clear()
11695{
11696 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
11697 if (data)
11698 data->m_hashMap.clear();
11699}
11700
32423dd8
JS
11701void wxRichTextFontTable::SetFontScale(double fontScale)
11702{
11703 if (fontScale != m_fontScale)
11704 Clear();
11705 m_fontScale = fontScale;
11706}
11707
24777478
JS
11708// wxTextBoxAttr
11709
24777478
JS
11710void wxTextBoxAttr::Reset()
11711{
11712 m_flags = 0;
603f702b
JS
11713 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
11714 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
11715 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
11716 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
2f987d83 11717 m_boxStyleName = wxEmptyString;
bec80f4f 11718
24777478
JS
11719 m_margins.Reset();
11720 m_padding.Reset();
11721 m_position.Reset();
11722
603f702b 11723 m_size.Reset();
303f0be7
JS
11724 m_minSize.Reset();
11725 m_maxSize.Reset();
24777478
JS
11726
11727 m_border.Reset();
11728 m_outline.Reset();
11729}
11730
11731// Equality test
11732bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
11733{
11734 return (
11735 m_flags == attr.m_flags &&
11736 m_floatMode == attr.m_floatMode &&
11737 m_clearMode == attr.m_clearMode &&
11738 m_collapseMode == attr.m_collapseMode &&
603f702b 11739 m_verticalAlignment == attr.m_verticalAlignment &&
bec80f4f 11740
24777478
JS
11741 m_margins == attr.m_margins &&
11742 m_padding == attr.m_padding &&
11743 m_position == attr.m_position &&
11744
603f702b 11745 m_size == attr.m_size &&
303f0be7
JS
11746 m_minSize == attr.m_minSize &&
11747 m_maxSize == attr.m_maxSize &&
24777478
JS
11748
11749 m_border == attr.m_border &&
2f987d83
JS
11750 m_outline == attr.m_outline &&
11751
11752 m_boxStyleName == attr.m_boxStyleName
24777478
JS
11753 );
11754}
11755
11756// Partial equality test
32423dd8 11757bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
24777478 11758{
32423dd8
JS
11759 if (!weakTest &&
11760 ((!HasFloatMode() && attr.HasFloatMode()) ||
11761 (!HasClearMode() && attr.HasClearMode()) ||
11762 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
11763 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
11764 (!HasBoxStyleName() && attr.HasBoxStyleName())))
11765 {
11766 return false;
11767 }
24777478
JS
11768 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
11769 return false;
11770
11771 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
11772 return false;
11773
11774 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
11775 return false;
11776
603f702b
JS
11777 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
11778 return false;
11779
2f987d83
JS
11780 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
11781 return false;
11782
24777478
JS
11783 // Position
11784
32423dd8 11785 if (!m_position.EqPartial(attr.m_position, weakTest))
24777478
JS
11786 return false;
11787
303f0be7
JS
11788 // Size
11789
32423dd8 11790 if (!m_size.EqPartial(attr.m_size, weakTest))
303f0be7 11791 return false;
32423dd8 11792 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
303f0be7 11793 return false;
32423dd8 11794 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
303f0be7
JS
11795 return false;
11796
24777478
JS
11797 // Margins
11798
32423dd8 11799 if (!m_margins.EqPartial(attr.m_margins, weakTest))
24777478
JS
11800 return false;
11801
11802 // Padding
11803
32423dd8 11804 if (!m_padding.EqPartial(attr.m_padding, weakTest))
24777478
JS
11805 return false;
11806
11807 // Border
11808
32423dd8 11809 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
24777478
JS
11810 return false;
11811
11812 // Outline
11813
32423dd8 11814 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
24777478
JS
11815 return false;
11816
11817 return true;
11818}
11819
11820// Merges the given attributes. If compareWith
11821// is non-NULL, then it will be used to mask out those attributes that are the same in style
11822// and compareWith, for situations where we don't want to explicitly set inherited attributes.
11823bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
11824{
11825 if (attr.HasFloatMode())
11826 {
11827 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
11828 SetFloatMode(attr.GetFloatMode());
11829 }
11830
11831 if (attr.HasClearMode())
11832 {
11833 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
11834 SetClearMode(attr.GetClearMode());
11835 }
11836
11837 if (attr.HasCollapseBorders())
11838 {
11839 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
603f702b
JS
11840 SetCollapseBorders(attr.GetCollapseBorders());
11841 }
11842
11843 if (attr.HasVerticalAlignment())
11844 {
11845 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
11846 SetVerticalAlignment(attr.GetVerticalAlignment());
24777478 11847 }
bec80f4f 11848
2f987d83
JS
11849 if (attr.HasBoxStyleName())
11850 {
11851 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
11852 SetBoxStyleName(attr.GetBoxStyleName());
11853 }
11854
bec80f4f
JS
11855 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
11856 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
11857 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
24777478 11858
603f702b 11859 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
303f0be7
JS
11860 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
11861 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
24777478 11862
bec80f4f
JS
11863 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
11864 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
24777478
JS
11865
11866 return true;
11867}
11868
11869// Remove specified attributes from this object
11870bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
11871{
11872 if (attr.HasFloatMode())
11873 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11874
11875 if (attr.HasClearMode())
11876 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11877
11878 if (attr.HasCollapseBorders())
11879 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11880
603f702b
JS
11881 if (attr.HasVerticalAlignment())
11882 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11883
2f987d83
JS
11884 if (attr.HasBoxStyleName())
11885 {
11886 SetBoxStyleName(wxEmptyString);
11887 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11888 }
11889
24777478
JS
11890 m_margins.RemoveStyle(attr.m_margins);
11891 m_padding.RemoveStyle(attr.m_padding);
11892 m_position.RemoveStyle(attr.m_position);
11893
603f702b 11894 m_size.RemoveStyle(attr.m_size);
303f0be7
JS
11895 m_minSize.RemoveStyle(attr.m_minSize);
11896 m_maxSize.RemoveStyle(attr.m_maxSize);
24777478
JS
11897
11898 m_border.RemoveStyle(attr.m_border);
11899 m_outline.RemoveStyle(attr.m_outline);
11900
11901 return true;
11902}
11903
11904// Collects the attributes that are common to a range of content, building up a note of
11905// which attributes are absent in some objects and which clash in some objects.
11906void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
11907{
11908 if (attr.HasFloatMode())
11909 {
11910 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
11911 {
11912 if (HasFloatMode())
11913 {
11914 if (GetFloatMode() != attr.GetFloatMode())
11915 {
11916 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
11917 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
11918 }
11919 }
11920 else
11921 SetFloatMode(attr.GetFloatMode());
11922 }
11923 }
11924 else
11925 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
bec80f4f 11926
24777478
JS
11927 if (attr.HasClearMode())
11928 {
11929 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
11930 {
11931 if (HasClearMode())
11932 {
11933 if (GetClearMode() != attr.GetClearMode())
11934 {
11935 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11936 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
11937 }
11938 }
11939 else
11940 SetClearMode(attr.GetClearMode());
11941 }
11942 }
11943 else
11944 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
11945
11946 if (attr.HasCollapseBorders())
11947 {
11948 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
11949 {
11950 if (HasCollapseBorders())
11951 {
11952 if (GetCollapseBorders() != attr.GetCollapseBorders())
11953 {
11954 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11955 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
11956 }
11957 }
11958 else
11959 SetCollapseBorders(attr.GetCollapseBorders());
11960 }
11961 }
11962 else
11963 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
bec80f4f 11964
603f702b
JS
11965 if (attr.HasVerticalAlignment())
11966 {
11967 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
11968 {
11969 if (HasVerticalAlignment())
11970 {
11971 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
11972 {
11973 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11974 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11975 }
11976 }
11977 else
11978 SetVerticalAlignment(attr.GetVerticalAlignment());
11979 }
11980 }
11981 else
11982 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
11983
2f987d83
JS
11984 if (attr.HasBoxStyleName())
11985 {
11986 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
11987 {
11988 if (HasBoxStyleName())
11989 {
11990 if (GetBoxStyleName() != attr.GetBoxStyleName())
11991 {
11992 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11993 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
11994 }
11995 }
11996 else
11997 SetBoxStyleName(attr.GetBoxStyleName());
11998 }
11999 }
12000 else
12001 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12002
24777478
JS
12003 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12004 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12005 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12006
603f702b 12007 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
303f0be7
JS
12008 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12009 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
24777478
JS
12010
12011 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12012 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12013}
12014
eb3d8a33
JS
12015bool wxTextBoxAttr::IsDefault() const
12016{
12017 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
303f0be7 12018 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
eb3d8a33
JS
12019 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12020}
12021
24777478
JS
12022// wxRichTextAttr
12023
12024void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12025{
bec80f4f
JS
12026 wxTextAttr::Copy(attr);
12027
24777478
JS
12028 m_textBoxAttr = attr.m_textBoxAttr;
12029}
12030
12031bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12032{
12033 if (!(wxTextAttr::operator==(attr)))
12034 return false;
bec80f4f 12035
24777478
JS
12036 return (m_textBoxAttr == attr.m_textBoxAttr);
12037}
12038
32423dd8
JS
12039// Partial equality test
12040bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
24777478 12041{
32423dd8 12042 if (!(wxTextAttr::EqPartial(attr, weakTest)))
24777478 12043 return false;
bec80f4f 12044
32423dd8 12045 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
24777478
JS
12046}
12047
12048// Merges the given attributes. If compareWith
12049// is non-NULL, then it will be used to mask out those attributes that are the same in style
12050// and compareWith, for situations where we don't want to explicitly set inherited attributes.
12051bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12052{
12053 wxTextAttr::Apply(style, compareWith);
12054
12055 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12056}
12057
12058// Remove specified attributes from this object
12059bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12060{
12061 wxTextAttr::RemoveStyle(*this, attr);
12062
12063 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12064}
12065
12066// Collects the attributes that are common to a range of content, building up a note of
12067// which attributes are absent in some objects and which clash in some objects.
12068void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12069{
12070 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
bec80f4f 12071
24777478
JS
12072 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12073}
12074
12075// Partial equality test
32423dd8 12076bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
24777478 12077{
32423dd8
JS
12078 if (!weakTest &&
12079 ((!HasStyle() && border.HasStyle()) ||
12080 (!HasColour() && border.HasColour()) ||
12081 (!HasWidth() && border.HasWidth())))
12082 {
12083 return false;
12084 }
12085
12086 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
24777478
JS
12087 return false;
12088
32423dd8 12089 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
24777478
JS
12090 return false;
12091
32423dd8 12092 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
24777478
JS
12093 return false;
12094
12095 return true;
12096}
12097
12098// Apply border to 'this', but not if the same as compareWith
bec80f4f 12099bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
24777478
JS
12100{
12101 if (border.HasStyle())
12102 {
12103 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12104 SetStyle(border.GetStyle());
12105 }
12106 if (border.HasColour())
12107 {
12108 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12109 SetColour(border.GetColourLong());
12110 }
12111 if (border.HasWidth())
12112 {
12113 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12114 SetWidth(border.GetWidth());
12115 }
12116
12117 return true;
12118}
12119
12120// Remove specified attributes from this object
bec80f4f 12121bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
24777478
JS
12122{
12123 if (attr.HasStyle() && HasStyle())
12124 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12125 if (attr.HasColour() && HasColour())
12126 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12127 if (attr.HasWidth() && HasWidth())
12128 m_borderWidth.Reset();
12129
12130 return true;
12131}
12132
12133// Collects the attributes that are common to a range of content, building up a note of
12134// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12135void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
24777478
JS
12136{
12137 if (attr.HasStyle())
12138 {
12139 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12140 {
12141 if (HasStyle())
12142 {
12143 if (GetStyle() != attr.GetStyle())
12144 {
12145 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12146 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12147 }
12148 }
12149 else
12150 SetStyle(attr.GetStyle());
12151 }
12152 }
12153 else
12154 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12155
12156 if (attr.HasColour())
12157 {
12158 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12159 {
12160 if (HasColour())
12161 {
12162 if (GetColour() != attr.GetColour())
12163 {
12164 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12165 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12166 }
12167 }
12168 else
12169 SetColour(attr.GetColourLong());
12170 }
12171 }
12172 else
12173 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
bec80f4f 12174
24777478
JS
12175 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12176}
12177
12178// Partial equality test
32423dd8 12179bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
24777478 12180{
32423dd8
JS
12181 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12182 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
24777478
JS
12183}
12184
12185// Apply border to 'this', but not if the same as compareWith
bec80f4f 12186bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
24777478 12187{
bec80f4f
JS
12188 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12189 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12190 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12191 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
24777478
JS
12192 return true;
12193}
12194
12195// Remove specified attributes from this object
bec80f4f 12196bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
24777478
JS
12197{
12198 m_left.RemoveStyle(attr.m_left);
12199 m_right.RemoveStyle(attr.m_right);
12200 m_top.RemoveStyle(attr.m_top);
12201 m_bottom.RemoveStyle(attr.m_bottom);
12202 return true;
12203}
12204
12205// Collects the attributes that are common to a range of content, building up a note of
12206// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12207void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
24777478
JS
12208{
12209 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12210 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12211 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12212 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12213}
12214
12215// Set style of all borders
bec80f4f 12216void wxTextAttrBorders::SetStyle(int style)
24777478
JS
12217{
12218 m_left.SetStyle(style);
12219 m_right.SetStyle(style);
12220 m_top.SetStyle(style);
12221 m_bottom.SetStyle(style);
12222}
12223
12224// Set colour of all borders
bec80f4f 12225void wxTextAttrBorders::SetColour(unsigned long colour)
24777478
JS
12226{
12227 m_left.SetColour(colour);
12228 m_right.SetColour(colour);
12229 m_top.SetColour(colour);
12230 m_bottom.SetColour(colour);
12231}
12232
bec80f4f 12233void wxTextAttrBorders::SetColour(const wxColour& colour)
24777478
JS
12234{
12235 m_left.SetColour(colour);
12236 m_right.SetColour(colour);
12237 m_top.SetColour(colour);
12238 m_bottom.SetColour(colour);
12239}
12240
12241// Set width of all borders
bec80f4f 12242void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
24777478
JS
12243{
12244 m_left.SetWidth(width);
12245 m_right.SetWidth(width);
12246 m_top.SetWidth(width);
12247 m_bottom.SetWidth(width);
12248}
12249
12250// Partial equality test
32423dd8 12251bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
24777478 12252{
32423dd8
JS
12253 if (!weakTest && !IsValid() && dim.IsValid())
12254 return false;
12255
603f702b 12256 if (dim.IsValid() && IsValid() && !((*this) == dim))
24777478
JS
12257 return false;
12258 else
12259 return true;
12260}
12261
12262bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12263{
603f702b 12264 if (dim.IsValid())
24777478
JS
12265 {
12266 if (!(compareWith && dim == (*compareWith)))
12267 (*this) = dim;
12268 }
12269
12270 return true;
12271}
12272
12273// Collects the attributes that are common to a range of content, building up a note of
12274// which attributes are absent in some objects and which clash in some objects.
12275void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12276{
603f702b 12277 if (attr.IsValid())
24777478 12278 {
603f702b 12279 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
24777478 12280 {
603f702b 12281 if (IsValid())
24777478
JS
12282 {
12283 if (!((*this) == attr))
12284 {
603f702b
JS
12285 clashingAttr.SetValid(true);
12286 SetValid(false);
24777478
JS
12287 }
12288 }
12289 else
12290 (*this) = attr;
12291 }
12292 }
12293 else
603f702b 12294 absentAttr.SetValid(true);
24777478
JS
12295}
12296
8995db52
JS
12297wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12298{
12299 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12300}
12301
12302wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12303{
12304 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12305}
12306
bec80f4f
JS
12307int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12308{
12309 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12310}
12311
12312int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12313{
12314 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12315}
12316
12317int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12318{
12319 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12320 return ConvertTenthsMMToPixels(dim.GetValue());
12321 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12322 return dim.GetValue();
12323 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12324 {
12325 wxASSERT(m_parentSize != wxDefaultSize);
12326 if (direction == wxHORIZONTAL)
12327 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12328 else
12329 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12330 }
12331 else
12332 {
12333 wxASSERT(false);
12334 return 0;
12335 }
12336}
12337
12338int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12339{
12340 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12341 return dim.GetValue();
12342 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12343 return ConvertPixelsToTenthsMM(dim.GetValue());
12344 else
12345 {
12346 wxASSERT(false);
12347 return 0;
12348 }
12349}
12350
24777478 12351// Partial equality test
32423dd8 12352bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
24777478 12353{
32423dd8 12354 if (!m_left.EqPartial(dims.m_left, weakTest))
24777478
JS
12355 return false;
12356
32423dd8 12357 if (!m_right.EqPartial(dims.m_right, weakTest))
24777478
JS
12358 return false;
12359
32423dd8 12360 if (!m_top.EqPartial(dims.m_top, weakTest))
24777478
JS
12361 return false;
12362
32423dd8 12363 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
24777478
JS
12364 return false;
12365
12366 return true;
12367}
12368
12369// Apply border to 'this', but not if the same as compareWith
bec80f4f 12370bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
24777478
JS
12371{
12372 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12373 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12374 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12375 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12376
12377 return true;
12378}
12379
12380// Remove specified attributes from this object
bec80f4f 12381bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
24777478 12382{
603f702b 12383 if (attr.m_left.IsValid())
24777478 12384 m_left.Reset();
603f702b 12385 if (attr.m_right.IsValid())
24777478 12386 m_right.Reset();
603f702b 12387 if (attr.m_top.IsValid())
24777478 12388 m_top.Reset();
603f702b 12389 if (attr.m_bottom.IsValid())
24777478
JS
12390 m_bottom.Reset();
12391
12392 return true;
12393}
12394
12395// Collects the attributes that are common to a range of content, building up a note of
12396// which attributes are absent in some objects and which clash in some objects.
bec80f4f 12397void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
24777478
JS
12398{
12399 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12400 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12401 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12402 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12403}
12404
603f702b 12405// Partial equality test
32423dd8 12406bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
603f702b 12407{
32423dd8 12408 if (!m_width.EqPartial(size.m_width, weakTest))
603f702b
JS
12409 return false;
12410
32423dd8 12411 if (!m_height.EqPartial(size.m_height, weakTest))
603f702b
JS
12412 return false;
12413
12414 return true;
12415}
12416
12417// Apply border to 'this', but not if the same as compareWith
12418bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12419{
12420 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12421 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12422
12423 return true;
12424}
12425
12426// Remove specified attributes from this object
12427bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12428{
12429 if (attr.m_width.IsValid())
12430 m_width.Reset();
12431 if (attr.m_height.IsValid())
12432 m_height.Reset();
12433
12434 return true;
12435}
12436
12437// Collects the attributes that are common to a range of content, building up a note of
12438// which attributes are absent in some objects and which clash in some objects.
12439void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12440{
12441 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12442 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12443}
12444
24777478
JS
12445// Collects the attributes that are common to a range of content, building up a note of
12446// which attributes are absent in some objects and which clash in some objects.
12447void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12448{
12449 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12450 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12451
12452 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12453
12454 if (attr.HasFont())
12455 {
340ef5c5
JS
12456 // If different font size units are being used, this is a clash.
12457 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
24777478 12458 {
340ef5c5
JS
12459 currentStyle.SetFontSize(0);
12460 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12461 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12462 }
12463 else
12464 {
12465 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
24777478 12466 {
340ef5c5 12467 if (currentStyle.HasFontPointSize())
24777478 12468 {
340ef5c5
JS
12469 if (currentStyle.GetFontSize() != attr.GetFontSize())
12470 {
12471 // Clash of attr - mark as such
12472 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12473 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12474 }
24777478 12475 }
340ef5c5
JS
12476 else
12477 currentStyle.SetFontSize(attr.GetFontSize());
24777478 12478 }
24777478 12479
340ef5c5 12480 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
32423dd8 12481 {
340ef5c5 12482 if (currentStyle.HasFontPixelSize())
32423dd8 12483 {
340ef5c5
JS
12484 if (currentStyle.GetFontSize() != attr.GetFontSize())
12485 {
12486 // Clash of attr - mark as such
12487 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12488 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12489 }
32423dd8 12490 }
340ef5c5
JS
12491 else
12492 currentStyle.SetFontPixelSize(attr.GetFontSize());
32423dd8 12493 }
32423dd8
JS
12494 }
12495
24777478
JS
12496 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12497 {
12498 if (currentStyle.HasFontItalic())
12499 {
12500 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
12501 {
12502 // Clash of attr - mark as such
12503 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12504 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12505 }
12506 }
12507 else
12508 currentStyle.SetFontStyle(attr.GetFontStyle());
12509 }
12510
12511 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12512 {
12513 if (currentStyle.HasFontFamily())
12514 {
12515 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
12516 {
12517 // Clash of attr - mark as such
12518 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12519 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12520 }
12521 }
12522 else
12523 currentStyle.SetFontFamily(attr.GetFontFamily());
12524 }
12525
12526 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12527 {
12528 if (currentStyle.HasFontWeight())
12529 {
12530 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
12531 {
12532 // Clash of attr - mark as such
12533 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12534 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12535 }
12536 }
12537 else
12538 currentStyle.SetFontWeight(attr.GetFontWeight());
12539 }
12540
12541 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12542 {
12543 if (currentStyle.HasFontFaceName())
12544 {
12545 wxString faceName1(currentStyle.GetFontFaceName());
12546 wxString faceName2(attr.GetFontFaceName());
12547
12548 if (faceName1 != faceName2)
12549 {
12550 // Clash of attr - mark as such
12551 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
12552 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
12553 }
12554 }
12555 else
12556 currentStyle.SetFontFaceName(attr.GetFontFaceName());
12557 }
12558
12559 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
12560 {
12561 if (currentStyle.HasFontUnderlined())
12562 {
12563 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
12564 {
12565 // Clash of attr - mark as such
12566 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12567 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12568 }
12569 }
12570 else
12571 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
12572 }
32423dd8
JS
12573
12574 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
12575 {
12576 if (currentStyle.HasFontStrikethrough())
12577 {
12578 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
12579 {
12580 // Clash of attr - mark as such
12581 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12582 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12583 }
12584 }
12585 else
12586 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
12587 }
24777478
JS
12588 }
12589
12590 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
12591 {
12592 if (currentStyle.HasTextColour())
12593 {
12594 if (currentStyle.GetTextColour() != attr.GetTextColour())
12595 {
12596 // Clash of attr - mark as such
12597 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
12598 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
12599 }
12600 }
12601 else
12602 currentStyle.SetTextColour(attr.GetTextColour());
12603 }
12604
12605 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
12606 {
12607 if (currentStyle.HasBackgroundColour())
12608 {
12609 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
12610 {
12611 // Clash of attr - mark as such
12612 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
12613 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
12614 }
12615 }
12616 else
12617 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
12618 }
12619
12620 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
12621 {
12622 if (currentStyle.HasAlignment())
12623 {
12624 if (currentStyle.GetAlignment() != attr.GetAlignment())
12625 {
12626 // Clash of attr - mark as such
12627 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
12628 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
12629 }
12630 }
12631 else
12632 currentStyle.SetAlignment(attr.GetAlignment());
12633 }
12634
12635 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
12636 {
12637 if (currentStyle.HasTabs())
12638 {
12639 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
12640 {
12641 // Clash of attr - mark as such
12642 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
12643 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
12644 }
12645 }
12646 else
12647 currentStyle.SetTabs(attr.GetTabs());
12648 }
12649
12650 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
12651 {
12652 if (currentStyle.HasLeftIndent())
12653 {
12654 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
12655 {
12656 // Clash of attr - mark as such
12657 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
12658 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
12659 }
12660 }
12661 else
12662 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
12663 }
12664
12665 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
12666 {
12667 if (currentStyle.HasRightIndent())
12668 {
12669 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
12670 {
12671 // Clash of attr - mark as such
12672 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
12673 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
12674 }
12675 }
12676 else
12677 currentStyle.SetRightIndent(attr.GetRightIndent());
12678 }
12679
12680 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
12681 {
12682 if (currentStyle.HasParagraphSpacingAfter())
12683 {
12684 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
12685 {
12686 // Clash of attr - mark as such
12687 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
12688 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
12689 }
12690 }
12691 else
12692 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
12693 }
12694
12695 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
12696 {
12697 if (currentStyle.HasParagraphSpacingBefore())
12698 {
12699 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
12700 {
12701 // Clash of attr - mark as such
12702 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
12703 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
12704 }
12705 }
12706 else
12707 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
12708 }
12709
12710 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
12711 {
12712 if (currentStyle.HasLineSpacing())
12713 {
12714 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
12715 {
12716 // Clash of attr - mark as such
12717 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
12718 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
12719 }
12720 }
12721 else
12722 currentStyle.SetLineSpacing(attr.GetLineSpacing());
12723 }
12724
12725 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
12726 {
12727 if (currentStyle.HasCharacterStyleName())
12728 {
12729 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
12730 {
12731 // Clash of attr - mark as such
12732 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
12733 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
12734 }
12735 }
12736 else
12737 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
12738 }
12739
12740 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
12741 {
12742 if (currentStyle.HasParagraphStyleName())
12743 {
12744 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
12745 {
12746 // Clash of attr - mark as such
12747 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
12748 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
12749 }
12750 }
12751 else
12752 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
12753 }
12754
12755 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
12756 {
12757 if (currentStyle.HasListStyleName())
12758 {
12759 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
12760 {
12761 // Clash of attr - mark as such
12762 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
12763 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
12764 }
12765 }
12766 else
12767 currentStyle.SetListStyleName(attr.GetListStyleName());
12768 }
12769
12770 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
12771 {
12772 if (currentStyle.HasBulletStyle())
12773 {
12774 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
12775 {
12776 // Clash of attr - mark as such
12777 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
12778 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
12779 }
12780 }
12781 else
12782 currentStyle.SetBulletStyle(attr.GetBulletStyle());
12783 }
12784
12785 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
12786 {
12787 if (currentStyle.HasBulletNumber())
12788 {
12789 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
12790 {
12791 // Clash of attr - mark as such
12792 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
12793 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
12794 }
12795 }
12796 else
12797 currentStyle.SetBulletNumber(attr.GetBulletNumber());
12798 }
12799
12800 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
12801 {
12802 if (currentStyle.HasBulletText())
12803 {
12804 if (currentStyle.GetBulletText() != attr.GetBulletText())
12805 {
12806 // Clash of attr - mark as such
12807 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
12808 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
12809 }
12810 }
12811 else
12812 {
12813 currentStyle.SetBulletText(attr.GetBulletText());
12814 currentStyle.SetBulletFont(attr.GetBulletFont());
12815 }
12816 }
12817
12818 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
12819 {
12820 if (currentStyle.HasBulletName())
12821 {
12822 if (currentStyle.GetBulletName() != attr.GetBulletName())
12823 {
12824 // Clash of attr - mark as such
12825 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
12826 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
12827 }
12828 }
12829 else
12830 {
12831 currentStyle.SetBulletName(attr.GetBulletName());
12832 }
12833 }
12834
12835 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
12836 {
12837 if (currentStyle.HasURL())
12838 {
12839 if (currentStyle.GetURL() != attr.GetURL())
12840 {
12841 // Clash of attr - mark as such
12842 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
12843 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
12844 }
12845 }
12846 else
12847 {
12848 currentStyle.SetURL(attr.GetURL());
12849 }
12850 }
12851
12852 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
12853 {
12854 if (currentStyle.HasTextEffects())
12855 {
12856 // We need to find the bits in the new attr that are different:
12857 // just look at those bits that are specified by the new attr.
12858
12859 // We need to remove the bits and flags that are not common between current attr
12860 // and new attr. In so doing we need to take account of the styles absent from one or more of the
12861 // previous styles.
12862
12863 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
12864 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
12865
12866 if (currentRelevantTextEffects != newRelevantTextEffects)
12867 {
12868 // Find the text effects that were different, using XOR
12869 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
12870
12871 // Clash of attr - mark as such
12872 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
12873 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
12874 }
12875 }
12876 else
12877 {
12878 currentStyle.SetTextEffects(attr.GetTextEffects());
12879 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
12880 }
12881
12882 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
12883 // that we've looked at so far
12884 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
12885 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
12886
12887 if (currentStyle.GetTextEffectFlags() == 0)
12888 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
12889 }
12890
12891 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
12892 {
12893 if (currentStyle.HasOutlineLevel())
12894 {
12895 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
12896 {
12897 // Clash of attr - mark as such
12898 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12899 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
12900 }
12901 }
12902 else
12903 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
12904 }
12905}
12906
bec80f4f
JS
12907WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
12908
12909IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
12910
12911bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
12912{
12913 if (m_properties.GetCount() != props.GetCount())
12914 return false;
12915
12916 size_t i;
12917 for (i = 0; i < m_properties.GetCount(); i++)
12918 {
12919 const wxVariant& var1 = m_properties[i];
12920 int idx = props.Find(var1.GetName());
12921 if (idx == -1)
12922 return false;
12923 const wxVariant& var2 = props.m_properties[idx];
12924 if (!(var1 == var2))
12925 return false;
12926 }
12927
12928 return true;
12929}
12930
12931wxArrayString wxRichTextProperties::GetPropertyNames() const
12932{
12933 wxArrayString arr;
12934 size_t i;
12935 for (i = 0; i < m_properties.GetCount(); i++)
12936 {
12937 arr.Add(m_properties[i].GetName());
12938 }
12939 return arr;
12940}
12941
12942int wxRichTextProperties::Find(const wxString& name) const
12943{
12944 size_t i;
12945 for (i = 0; i < m_properties.GetCount(); i++)
12946 {
12947 if (m_properties[i].GetName() == name)
12948 return (int) i;
12949 }
12950 return -1;
12951}
12952
590a0f8b
JS
12953bool wxRichTextProperties::Remove(const wxString& name)
12954{
12955 int idx = Find(name);
12956 if (idx != -1)
12957 {
12958 m_properties.RemoveAt(idx);
12959 return true;
12960 }
12961 else
12962 return false;
12963}
12964
bec80f4f
JS
12965wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
12966{
12967 int idx = Find(name);
12968 if (idx == wxNOT_FOUND)
12969 SetProperty(name, wxString());
12970 idx = Find(name);
12971 if (idx != wxNOT_FOUND)
12972 {
12973 return & (*this)[idx];
12974 }
12975 else
12976 return NULL;
12977}
12978
12979const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
12980{
12981 static const wxVariant nullVariant;
12982 int idx = Find(name);
12983 if (idx != -1)
12984 return m_properties[idx];
12985 else
12986 return nullVariant;
12987}
12988
12989wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
12990{
12991 return GetProperty(name).GetString();
12992}
12993
12994long wxRichTextProperties::GetPropertyLong(const wxString& name) const
12995{
12996 return GetProperty(name).GetLong();
12997}
12998
12999bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13000{
13001 return GetProperty(name).GetBool();
13002}
13003
13004double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13005{
13006 return GetProperty(name).GetDouble();
13007}
13008
13009void wxRichTextProperties::SetProperty(const wxVariant& variant)
13010{
13011 wxASSERT(!variant.GetName().IsEmpty());
13012
13013 int idx = Find(variant.GetName());
13014
13015 if (idx == -1)
13016 m_properties.Add(variant);
13017 else
13018 m_properties[idx] = variant;
13019}
13020
13021void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13022{
13023 int idx = Find(name);
13024 wxVariant var(variant);
13025 var.SetName(name);
13026
13027 if (idx == -1)
13028 m_properties.Add(var);
13029 else
13030 m_properties[idx] = var;
13031}
13032
13033void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13034{
13035 SetProperty(name, wxVariant(value, name));
13036}
13037
13038void wxRichTextProperties::SetProperty(const wxString& name, long value)
13039{
13040 SetProperty(name, wxVariant(value, name));
13041}
13042
13043void wxRichTextProperties::SetProperty(const wxString& name, double value)
13044{
13045 SetProperty(name, wxVariant(value, name));
13046}
13047
13048void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13049{
13050 SetProperty(name, wxVariant(value, name));
13051}
24777478 13052
590a0f8b
JS
13053void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13054{
13055 size_t i;
13056 for (i = 0; i < properties.GetCount(); i++)
13057 {
13058 wxString name = properties.GetProperties()[i].GetName();
13059 if (HasProperty(name))
13060 Remove(name);
13061 }
13062}
13063
13064void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13065{
13066 size_t i;
13067 for (i = 0; i < properties.GetCount(); i++)
13068 {
13069 SetProperty(properties.GetProperties()[i]);
13070 }
13071}
13072
603f702b
JS
13073wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13074{
13075 if (m_address.GetCount() == 0)
13076 return topLevelContainer;
13077
13078 wxRichTextCompositeObject* p = topLevelContainer;
13079 size_t i = 0;
13080 while (p && i < m_address.GetCount())
13081 {
13082 int pos = m_address[i];
13083 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13084 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13085 return NULL;
13086
13087 wxRichTextObject* p1 = p->GetChild(pos);
13088 if (i == (m_address.GetCount()-1))
13089 return p1;
13090
13091 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13092 i ++;
13093 }
13094 return NULL;
13095}
13096
13097bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13098{
13099 m_address.Clear();
13100
13101 if (topLevelContainer == obj)
13102 return true;
13103
13104 wxRichTextObject* o = obj;
13105 while (o)
13106 {
13107 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13108 if (!p)
13109 return false;
13110
13111 int pos = p->GetChildren().IndexOf(o);
13112 if (pos == -1)
13113 return false;
13114
13115 m_address.Insert(pos, 0);
13116
13117 if (p == topLevelContainer)
13118 return true;
13119
13120 o = p;
13121 }
13122 return false;
13123}
13124
13125// Equality test
13126bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13127{
13128 if (m_container != sel.m_container)
13129 return false;
13130 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13131 return false;
13132 size_t i;
13133 for (i = 0; i < m_ranges.GetCount(); i++)
13134 if (!(m_ranges[i] == sel.m_ranges[i]))
13135 return false;
13136 return true;
13137}
13138
13139// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13140// or none at the level of the object's container.
13141wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13142{
13143 if (IsValid())
13144 {
13145 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13146
13147 if (container == m_container)
13148 return m_ranges;
13149
13150 container = obj->GetContainer();
13151 while (container)
13152 {
13153 if (container->GetParent())
13154 {
13155 // If we found that our object's container is within the range of
13156 // a selection higher up, then assume the whole original object
13157 // is also selected.
13158 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13159 if (parentContainer == m_container)
13160 {
13161 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13162 {
13163 wxRichTextRangeArray ranges;
13164 ranges.Add(obj->GetRange());
13165 return ranges;
13166 }
13167 }
13168
13169 container = parentContainer;
13170 }
13171 else
13172 {
13173 container = NULL;
13174 break;
13175 }
13176 }
13177 }
13178 return wxRichTextRangeArray();
13179}
13180
13181// Is the given position within the selection?
13182bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13183{
13184 if (!IsValid())
13185 return false;
13186 else
13187 {
13188 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13189 return WithinSelection(pos, selectionRanges);
13190 }
13191}
13192
13193// Is the given position within the selection range?
13194bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13195{
13196 size_t i;
13197 for (i = 0; i < ranges.GetCount(); i++)
13198 {
13199 const wxRichTextRange& range = ranges[i];
13200 if (pos >= range.GetStart() && pos <= range.GetEnd())
13201 return true;
13202 }
13203 return false;
13204}
13205
13206// Is the given range completely within the selection range?
13207bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13208{
13209 size_t i;
13210 for (i = 0; i < ranges.GetCount(); i++)
13211 {
13212 const wxRichTextRange& eachRange = ranges[i];
13213 if (range.IsWithin(eachRange))
13214 return true;
13215 }
13216 return false;
13217}
13218
8db2e3ef
JS
13219IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13220IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13221
13222bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13223{
13224 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13225 while (node)
13226 {
13227 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13228 if (handler->HasVirtualAttributes(obj))
13229 return true;
13230
13231 node = node->GetNext();
13232 }
13233 return false;
13234}
13235
13236wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13237{
13238 wxRichTextAttr attr;
13239 // We apply all handlers, so we can may combine several different attributes
13240 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13241 while (node)
13242 {
13243 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13244 if (handler->HasVirtualAttributes(obj))
13245 {
13246 bool success = handler->GetVirtualAttributes(attr, obj);
13247 wxASSERT(success);
aa8f57f4 13248 wxUnusedVar(success);
8db2e3ef
JS
13249 }
13250
13251 node = node->GetNext();
13252 }
13253 return attr;
13254}
13255
13256bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13257{
13258 if (HasVirtualAttributes(obj))
13259 {
13260 wxRichTextAttr a(GetVirtualAttributes(obj));
13261 attr.Apply(a);
13262 return true;
13263 }
13264 else
13265 return false;
13266}
13267
13268/// Adds a handler to the end
13269void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13270{
13271 sm_drawingHandlers.Append(handler);
13272}
13273
13274/// Inserts a handler at the front
13275void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13276{
13277 sm_drawingHandlers.Insert( handler );
13278}
13279
13280/// Removes a handler
13281bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13282{
13283 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13284 if (handler)
13285 {
13286 sm_drawingHandlers.DeleteObject(handler);
13287 delete handler;
13288 return true;
13289 }
13290 else
13291 return false;
13292}
13293
13294wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13295{
13296 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13297 while (node)
13298 {
13299 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13300 if (handler->GetName().Lower() == name.Lower()) return handler;
13301
13302 node = node->GetNext();
13303 }
13304 return NULL;
13305}
13306
13307void wxRichTextBuffer::CleanUpDrawingHandlers()
13308{
13309 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13310 while (node)
13311 {
13312 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13313 wxList::compatibility_iterator next = node->GetNext();
13314 delete handler;
13315 node = next;
13316 }
13317
13318 sm_drawingHandlers.Clear();
13319}
603f702b 13320
7c9fdebe
JS
13321void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13322{
13323 sm_fieldTypes[fieldType->GetName()] = fieldType;
13324}
13325
13326bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13327{
13328 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13329 if (it == sm_fieldTypes.end())
13330 return false;
13331 else
13332 {
13333 wxRichTextFieldType* fieldType = it->second;
13334 sm_fieldTypes.erase(it);
13335 delete fieldType;
13336 return true;
13337 }
13338}
13339
13340wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13341{
13342 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13343 if (it == sm_fieldTypes.end())
13344 return NULL;
13345 else
13346 return it->second;
13347}
13348
13349void wxRichTextBuffer::CleanUpFieldTypes()
13350{
13351 wxRichTextFieldTypeHashMap::iterator it;
13352 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
13353 {
13354 wxRichTextFieldType* fieldType = it->second;
13355 delete fieldType;
13356 }
13357
13358 sm_fieldTypes.clear();
13359}
13360
5d7836c4
JS
13361#endif
13362 // wxUSE_RICHTEXT