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