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