Added support for sub-object virtual attributes (temporary attributes for characters...
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT
20
21 #include "wx/richtext/richtextbuffer.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/dc.h"
25 #include "wx/intl.h"
26 #include "wx/log.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
29 #endif
30
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
40
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
46
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
49
50 WX_DEFINE_LIST(wxRichTextObjectList)
51 WX_DEFINE_LIST(wxRichTextLineList)
52
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58
59 const wxChar wxRichTextLineBreakChar = (wxChar) 29;
60
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
63 {
64 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
65 {
66 startY = sY;
67 endY = eY;
68 width = w;
69 anchor = obj;
70 }
71
72 int startY, endY;
73 int width;
74 wxRichTextObject* anchor;
75 };
76
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
78
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
80 {
81 return r1->startY - r2->startY;
82 }
83
84 class wxRichTextFloatCollector
85 {
86 public:
87 wxRichTextFloatCollector(const wxRect& availableRect);
88 ~wxRichTextFloatCollector();
89
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph* para);
92 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
93
94 // Return the last paragraph we collected
95 wxRichTextParagraph* LastParagraph();
96
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect GetAvailableRect(int startY, int endY);
100
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction, int start, int height) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
104
105 // Find the last y position
106 int GetLastRectBottom();
107
108 // Draw the floats inside a rect
109 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
110
111 // HitTest the floats
112 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
113
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
116
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
119
120 // Delete a float
121 bool DeleteFloat(wxRichTextObject* obj);
122
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject* obj);
125
126 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
127
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
129
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
131
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
133
134 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
135
136 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
137
138 private:
139 wxRichTextFloatRectMapArray m_left;
140 wxRichTextFloatRectMapArray m_right;
141 //int m_width;
142 wxRect m_availableRect;
143 wxRichTextParagraph* m_para;
144 };
145
146 // Delete a float
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
148 {
149 size_t i;
150 for (i = 0; i < m_left.GetCount(); i++)
151 {
152 if (m_left[i]->anchor == obj)
153 {
154 m_left.RemoveAt(i);
155 return true;
156 }
157 }
158 for (i = 0; i < m_right.GetCount(); i++)
159 {
160 if (m_right[i]->anchor == obj)
161 {
162 m_right.RemoveAt(i);
163 return true;
164 }
165 }
166 return false;
167 }
168
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
171 {
172 size_t i;
173 for (i = 0; i < m_left.GetCount(); i++)
174 {
175 if (m_left[i]->anchor == obj)
176 {
177 return true;
178 }
179 }
180 for (i = 0; i < m_right.GetCount(); i++)
181 {
182 if (m_right[i]->anchor == obj)
183 {
184 return true;
185 }
186 }
187 return false;
188 }
189
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
192 {
193 size_t i;
194 for (i = 0; i < m_left.GetCount(); i++)
195 objects.Append(m_left[i]->anchor);
196 for (i = 0; i < m_right.GetCount(); i++)
197 objects.Append(m_right[i]->anchor);
198 return true;
199 }
200
201
202 /*
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
207 */
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
209 {
210 int end = array.GetCount() - 1;
211 int start = 0;
212 int ret = 0;
213
214 wxASSERT(end >= 0);
215
216 while (true)
217 {
218 if (start > end)
219 {
220 break;
221 }
222
223 int mid = (start + end) / 2;
224 if (array[mid]->startY <= point && array[mid]->endY >= point)
225 return mid;
226 else if (array[mid]->startY > point)
227 {
228 end = mid - 1;
229 ret = mid;
230 }
231 else if (array[mid]->endY < point)
232 {
233 start = mid + 1;
234 ret = start;
235 }
236 }
237
238 return ret;
239 }
240
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
242 {
243 int ret = 0;
244 int len = array.GetCount();
245
246 wxASSERT(index >= 0 && index < len);
247
248 if (array[index]->startY < startY && array[index]->endY > startY)
249 ret = ret < array[index]->width ? array[index]->width : ret;
250 while (index < len && array[index]->startY <= endY)
251 {
252 ret = ret < array[index]->width ? array[index]->width : ret;
253 index++;
254 }
255
256 return ret;
257 }
258
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
260 {
261 m_availableRect = rect;
262 m_para = NULL;
263 }
264
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
266 {
267 int len = array.GetCount();
268 for (int i = 0; i < len; i++)
269 delete array[i];
270 }
271
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 {
274 FreeFloatRectMapArray(m_left);
275 FreeFloatRectMapArray(m_right);
276 }
277
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
279 {
280 if (array.GetCount() == 0)
281 return start;
282
283 int i = SearchAdjacentRect(array, start);
284 int last = start;
285 while (i < (int) array.GetCount())
286 {
287 if (array[i]->startY - last >= height)
288 return last + 1;
289 last = array[i]->endY;
290 i++;
291 }
292
293 return last + 1;
294 }
295
296 int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
297 {
298 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
299 return GetFitPosition(m_left, start, height);
300 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
301 return GetFitPosition(m_right, start, height);
302 else
303 {
304 wxASSERT("Never should be here");
305 return start;
306 }
307 }
308
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
312 {
313 int direction = floating->GetFloatDirection();
314
315 wxPoint pos = floating->GetPosition();
316 wxSize size = floating->GetCachedSize();
317 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
318 switch (direction)
319 {
320 case wxTEXT_BOX_ATTR_FLOAT_NONE:
321 delete map;
322 break;
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
326 m_left.Add(map);
327 break;
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
330 m_right.Add(map);
331 break;
332 default:
333 delete map;
334 wxASSERT("Unrecognised float attribute.");
335 }
336
337 m_para = para;
338 }
339
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
341 {
342 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
343 while (node)
344 {
345 wxRichTextObject* floating = node->GetData();
346
347 if (floating->IsFloating())
348 {
349 CollectFloat(para, floating);
350 }
351
352 node = node->GetNext();
353 }
354
355 m_para = para;
356 }
357
358 wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
359 {
360 return m_para;
361 }
362
363 wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
364 {
365 int widthLeft = 0, widthRight = 0;
366 if (m_left.GetCount() != 0)
367 {
368 int i = SearchAdjacentRect(m_left, startY);
369 if (i < (int) m_left.GetCount())
370 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
371 }
372 if (m_right.GetCount() != 0)
373 {
374 int j = SearchAdjacentRect(m_right, startY);
375 if (j < (int) m_right.GetCount())
376 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
377 }
378
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
383 }
384
385 int wxRichTextFloatCollector::GetLastRectBottom()
386 {
387 int ret = 0;
388 int len = m_left.GetCount();
389 if (len) {
390 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
391 }
392 len = m_right.GetCount();
393 if (len) {
394 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
395 }
396
397 return ret;
398 }
399
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
401 {
402 int start = rect.y;
403 int end = rect.y + rect.height;
404 int i, j;
405 i = SearchAdjacentRect(array, start);
406 if (i < 0 || i >= (int) array.GetCount())
407 return;
408 j = SearchAdjacentRect(array, end);
409 if (j < 0 || j >= (int) array.GetCount())
410 j = array.GetCount() - 1;
411 while (i <= j)
412 {
413 wxRichTextObject* obj = array[i]->anchor;
414 wxRichTextRange r = obj->GetRange();
415 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
416 i++;
417 }
418 }
419
420 void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
421 {
422 if (m_left.GetCount() > 0)
423 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
424 if (m_right.GetCount() > 0)
425 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
426 }
427
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
429 {
430 int i;
431 if (array.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE;
433 i = SearchAdjacentRect(array, pt.y);
434 if (i < 0 || i >= (int) array.GetCount())
435 return wxRICHTEXT_HITTEST_NONE;
436 if (!array[i]->anchor->IsShown())
437 return wxRICHTEXT_HITTEST_NONE;
438
439 wxPoint point = array[i]->anchor->GetPosition();
440 wxSize size = array[i]->anchor->GetCachedSize();
441 if (point.x <= pt.x && point.x + size.x >= pt.x
442 && point.y <= pt.y && point.y + size.y >= pt.y)
443 {
444 textPosition = array[i]->anchor->GetRange().GetStart();
445 * obj = array[i]->anchor;
446 if (pt.x > (pt.x + pt.x + size.x) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE;
448 else
449 return wxRICHTEXT_HITTEST_AFTER;
450 }
451
452 return wxRICHTEXT_HITTEST_NONE;
453 }
454
455 int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
456 {
457 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
458 if (ret == wxRICHTEXT_HITTEST_NONE)
459 {
460 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
461 }
462 return ret;
463 }
464
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
467 {
468 dc.SetFont(font);
469 }
470
471 inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
472 {
473 const wxPen& pen1 = dc.GetPen();
474 if (pen1.IsOk() && pen.IsOk())
475 {
476 if (pen1.GetWidth() == pen.GetWidth() &&
477 pen1.GetStyle() == pen.GetStyle() &&
478 pen1.GetColour() == pen.GetColour())
479 return;
480 }
481 dc.SetPen(pen);
482 }
483
484 inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
485 {
486 const wxBrush& brush1 = dc.GetBrush();
487 if (brush1.IsOk() && brush.IsOk())
488 {
489 if (brush1.GetStyle() == brush.GetStyle() &&
490 brush1.GetColour() == brush.GetColour())
491 return;
492 }
493 dc.SetBrush(brush);
494 }
495
496 /*!
497 * wxRichTextObject
498 * This is the base for drawable objects.
499 */
500
501 IMPLEMENT_CLASS(wxRichTextObject, wxObject)
502
503 wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
504 {
505 m_refCount = 1;
506 m_parent = parent;
507 m_descent = 0;
508 m_show = true;
509 }
510
511 wxRichTextObject::~wxRichTextObject()
512 {
513 }
514
515 void wxRichTextObject::Dereference()
516 {
517 m_refCount --;
518 if (m_refCount <= 0)
519 delete this;
520 }
521
522 /// Copy
523 void wxRichTextObject::Copy(const wxRichTextObject& obj)
524 {
525 m_size = obj.m_size;
526 m_maxSize = obj.m_maxSize;
527 m_minSize = obj.m_minSize;
528 m_pos = obj.m_pos;
529 m_range = obj.m_range;
530 m_ownRange = obj.m_ownRange;
531 m_attributes = obj.m_attributes;
532 m_properties = obj.m_properties;
533 m_descent = obj.m_descent;
534 m_show = obj.m_show;
535 }
536
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
539 {
540 const wxRichTextObject* p = this;
541 while (p)
542 {
543 if (p->IsTopLevel())
544 {
545 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
546 }
547 p = p->GetParent();
548 }
549 return NULL;
550 }
551
552 void wxRichTextObject::SetMargins(int margin)
553 {
554 SetMargins(margin, margin, margin, margin);
555 }
556
557 void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
558 {
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
563 }
564
565 int wxRichTextObject::GetLeftMargin() const
566 {
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
568 }
569
570 int wxRichTextObject::GetRightMargin() const
571 {
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
573 }
574
575 int wxRichTextObject::GetTopMargin() const
576 {
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
578 }
579
580 int wxRichTextObject::GetBottomMargin() const
581 {
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
583 }
584
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
588 {
589 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590 marginRect = outerRect;
591 wxRichTextAttr attr(GetAttributes());
592 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
594 return contentRect;
595 }
596
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
599 {
600 if (invalidRange != wxRICHTEXT_NONE)
601 {
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
606 SetCachedSize(wxDefaultSize);
607 SetMaxSize(wxDefaultSize);
608 SetMinSize(wxDefaultSize);
609 }
610 }
611
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
614 {
615 // Unscale
616 double scale = 1.0;
617 if (GetBuffer())
618 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
620
621 return p;
622 }
623
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
626 {
627 // There are ppi pixels in 254.1 "1/10 mm"
628
629 double pixels = ((double) units * (double)ppi) / 254.1;
630 if (scale != 1.0)
631 pixels /= scale;
632
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels == 0 && units > 0)
635 pixels = 1;
636
637 return (int) pixels;
638 }
639
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
642 {
643 int p = pixels;
644 double scale = 1.0;
645 if (GetBuffer())
646 scale = GetBuffer()->GetScale();
647
648 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
649 }
650
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
652 {
653 // There are ppi pixels in 254.1 "1/10 mm"
654
655 double p = double(pixels);
656
657 if (scale != 1.0)
658 p *= scale;
659
660 int units = int( p * 254.1 / (double) ppi );
661 return units;
662 }
663
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
667 {
668 // Assume boxRect is the area around the content
669 wxRect marginRect = boxRect;
670 wxRect contentRect, borderRect, paddingRect, outlineRect;
671
672 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
673
674 // Margin is transparent. Draw background from margin.
675 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
676 {
677 wxColour colour;
678 if (flags & wxRICHTEXT_DRAW_SELECTED)
679 {
680 // TODO: get selection colour from control?
681 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
682 }
683 else
684 colour = attr.GetBackgroundColour();
685
686 wxPen pen(colour);
687 wxBrush brush(colour);
688
689 dc.SetPen(pen);
690 dc.SetBrush(brush);
691 dc.DrawRectangle(borderRect);
692 }
693
694 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
695 {
696 wxRichTextAttr editBorderAttr = attr;
697 // TODO: make guideline colour configurable
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
701
702 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
703 }
704
705 if (attr.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
707
708 if (attr.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
710
711 return true;
712 }
713
714 // Draw a border
715 bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
716 {
717 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
718 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
719
720 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
721 {
722 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723 wxColour col(attr.GetLeft().GetColour());
724
725 // If pen width is > 1, resorts to a solid rectangle.
726 if (borderLeft == 1)
727 {
728 int penStyle = wxSOLID;
729 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
730 penStyle = wxDOT;
731 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732 penStyle = wxLONG_DASH;
733 wxPen pen(col, 1, penStyle);
734 dc.SetPen(pen);
735 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
736
737 }
738 else if (borderLeft > 1)
739 {
740 wxPen pen(col);
741 wxBrush brush(col);
742 dc.SetPen(pen);
743 dc.SetBrush(brush);
744 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
745 }
746 }
747
748 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
749 {
750 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
751
752 wxColour col(attr.GetRight().GetColour());
753
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight == 1)
756 {
757 int penStyle = wxSOLID;
758 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
759 penStyle = wxDOT;
760 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761 penStyle = wxLONG_DASH;
762 wxPen pen(col, 1, penStyle);
763 dc.SetPen(pen);
764 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
765
766 }
767 else if (borderRight > 1)
768 {
769 wxPen pen(col);
770 wxBrush brush(col);
771 dc.SetPen(pen);
772 dc.SetBrush(brush);
773 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
774 }
775 }
776
777 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
778 {
779 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
780
781 wxColour col(attr.GetTop().GetColour());
782
783 // If pen width is > 1, resorts to a solid rectangle.
784 if (borderTop == 1)
785 {
786 int penStyle = wxSOLID;
787 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
788 penStyle = wxDOT;
789 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790 penStyle = wxLONG_DASH;
791 wxPen pen(col, 1, penStyle);
792 dc.SetPen(pen);
793 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
794
795 }
796 else if (borderTop > 1)
797 {
798 wxPen pen(col);
799 wxBrush brush(col);
800 dc.SetPen(pen);
801 dc.SetBrush(brush);
802 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
803 }
804 }
805
806 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
807 {
808 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
809 wxColour col(attr.GetTop().GetColour());
810
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom == 1)
813 {
814 int penStyle = wxSOLID;
815 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
816 penStyle = wxDOT;
817 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818 penStyle = wxLONG_DASH;
819 wxPen pen(col, 1, penStyle);
820 dc.SetPen(pen);
821 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
822
823 }
824 else if (borderBottom > 1)
825 {
826 wxPen pen(col);
827 wxBrush brush(col);
828 dc.SetPen(pen);
829 dc.SetBrush(brush);
830 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
831 }
832 }
833
834 return true;
835 }
836
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
840 // is available.
841 //
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843
844 bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
845 {
846 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
850
851 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
852
853 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
861
862 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870
871 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
879
880 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888
889 int leftTotal = marginLeft + borderLeft + paddingLeft;
890 int rightTotal = marginRight + borderRight + paddingRight;
891 int topTotal = marginTop + borderTop + paddingTop;
892 int bottomTotal = marginBottom + borderBottom + paddingBottom;
893
894 if (marginRect != wxRect())
895 {
896 contentRect.x = marginRect.x + leftTotal;
897 contentRect.y = marginRect.y + topTotal;
898 contentRect.width = marginRect.width - (leftTotal + rightTotal);
899 contentRect.height = marginRect.height - (topTotal + bottomTotal);
900 }
901 else
902 {
903 marginRect.x = contentRect.x - leftTotal;
904 marginRect.y = contentRect.y - topTotal;
905 marginRect.width = contentRect.width + (leftTotal + rightTotal);
906 marginRect.height = contentRect.height + (topTotal + bottomTotal);
907 }
908
909 borderRect.x = marginRect.x + marginLeft;
910 borderRect.y = marginRect.y + marginTop;
911 borderRect.width = marginRect.width - (marginLeft + marginRight);
912 borderRect.height = marginRect.height - (marginTop + marginBottom);
913
914 paddingRect.x = marginRect.x + marginLeft + borderLeft;
915 paddingRect.y = marginRect.y + marginTop + borderTop;
916 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
918
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect.x = marginRect.x - outlineLeft;
921 outlineRect.y = marginRect.y - outlineTop;
922 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
924
925 return true;
926 }
927
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930 int& topMargin, int& bottomMargin)
931 {
932 // Assume boxRect is the area around the content
933 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934 marginRect = wxRect(0, 0, 1000, 1000);
935
936 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
937
938 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939 rightMargin = marginRect.GetRight() - contentRect.GetRight();
940 topMargin = contentRect.GetTop() - marginRect.GetTop();
941 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
942
943 return true;
944 }
945
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
951 {
952 wxRect rect = availableParentSpace;
953 double scale = 1.0;
954 if (buffer)
955 scale = buffer->GetScale();
956
957 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
958
959 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
961
962 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
964
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 {
969 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
970 }
971 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 {
973 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
975 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
976 else
977 rect.x += x;
978 }
979
980 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 {
982 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
983 }
984 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 {
986 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
988 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
989 else
990 rect.y += y;
991 }
992
993 if (rect.GetWidth() > availableParentSpace.GetWidth())
994 rect.SetWidth(availableParentSpace.GetWidth());
995
996 return rect;
997 }
998
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream& stream)
1001 {
1002 stream << GetClassInfo()->GetClassName() << wxT("\n");
1003 stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
1004 stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
1005 }
1006
1007 // Gets the containing buffer
1008 wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1009 {
1010 const wxRichTextObject* obj = this;
1011 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
1012 obj = obj->GetParent();
1013 return wxDynamicCast(obj, wxRichTextBuffer);
1014 }
1015
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint wxRichTextObject::GetAbsolutePosition() const
1018 {
1019 wxPoint pt = GetPosition();
1020
1021 wxRichTextObject* p = GetParent();
1022 while (p)
1023 {
1024 pt = pt + p->GetPosition();
1025 p = p->GetParent();
1026 }
1027
1028 return pt;
1029 }
1030
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
1034 {
1035 if (!IsShown())
1036 return wxRICHTEXT_HITTEST_NONE;
1037
1038 wxRect rect = GetRect();
1039 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040 pt.y >= rect.y && pt.y < rect.y + rect.height)
1041 {
1042 *obj = this;
1043 *contextObj = GetParentContainer();
1044 textPosition = GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON;
1046 }
1047 else
1048 return wxRICHTEXT_HITTEST_NONE;
1049 }
1050
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
1054 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
1056 int style)
1057 {
1058 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
1059 wxRect originalAvailableRect = availableChildRect;
1060 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1061
1062 wxSize maxSize = GetMaxSize();
1063
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 // on this basis
1066 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
1067 {
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL);
1070 wxRichTextAttr newAttr(attr);
1071 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1073
1074 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
1075
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1081 {
1082 // centering, right-justification
1083 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
1084 {
1085 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1086 }
1087 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
1088 {
1089 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1090 }
1091 }
1092
1093 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1094 }
1095
1096 /*
1097 __________________
1098 | ____________ |
1099 | | | |
1100
1101
1102 */
1103
1104 return true;
1105 }
1106
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint& pt)
1109 {
1110 SetPosition(pt);
1111 }
1112
1113
1114 /*!
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1117 */
1118
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1120
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1122 wxRichTextObject(parent)
1123 {
1124 }
1125
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1127 {
1128 DeleteChildren();
1129 }
1130
1131 /// Get the nth child
1132 wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1133 {
1134 wxASSERT ( n < m_children.GetCount() );
1135
1136 return m_children.Item(n)->GetData();
1137 }
1138
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1141 {
1142 m_children.Append(child);
1143 child->SetParent(this);
1144 return m_children.GetCount() - 1;
1145 }
1146
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1149 {
1150 if (inFrontOf)
1151 {
1152 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1153 m_children.Insert(node, child);
1154 }
1155 else
1156 m_children.Insert(child);
1157 child->SetParent(this);
1158
1159 return true;
1160 }
1161
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1164 {
1165 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1166 if (node)
1167 {
1168 wxRichTextObject* obj = node->GetData();
1169 m_children.Erase(node);
1170 if (deleteChild)
1171 delete obj;
1172
1173 return true;
1174 }
1175 return false;
1176 }
1177
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1180 {
1181 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1182 while (node)
1183 {
1184 wxRichTextObjectList::compatibility_iterator oldNode = node;
1185
1186 wxRichTextObject* child = node->GetData();
1187 child->Dereference(); // Only delete if reference count is zero
1188
1189 node = node->GetNext();
1190 m_children.Erase(oldNode);
1191 }
1192
1193 return true;
1194 }
1195
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1198 {
1199 return m_children.GetCount();
1200 }
1201
1202 /// Copy
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1204 {
1205 wxRichTextObject::Copy(obj);
1206
1207 DeleteChildren();
1208
1209 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1210 while (node)
1211 {
1212 wxRichTextObject* child = node->GetData();
1213 wxRichTextObject* newChild = child->Clone();
1214 newChild->SetParent(this);
1215 m_children.Append(newChild);
1216
1217 node = node->GetNext();
1218 }
1219 }
1220
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1224 {
1225 if (!IsShown())
1226 return wxRICHTEXT_HITTEST_NONE;
1227
1228 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1229 while (node)
1230 {
1231 wxRichTextObject* child = node->GetData();
1232
1233 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1234 {
1235 // Just check if we hit the overall object
1236 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1237 if (ret != wxRICHTEXT_HITTEST_NONE)
1238 return ret;
1239 }
1240 else if (child->IsShown())
1241 {
1242 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1243 if (ret != wxRICHTEXT_HITTEST_NONE)
1244 return ret;
1245 }
1246
1247 node = node->GetNext();
1248 }
1249
1250 return wxRICHTEXT_HITTEST_NONE;
1251 }
1252
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
1255 {
1256 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1257 while (node)
1258 {
1259 wxRichTextObject* child = node->GetData();
1260
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
1266 return true;
1267
1268 node = node->GetNext();
1269 }
1270
1271 return false;
1272 }
1273
1274 /// Calculate range
1275 void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1276 {
1277 long current = start;
1278 long lastEnd = current;
1279
1280 if (IsTopLevel())
1281 {
1282 current = 0;
1283 lastEnd = 0;
1284 }
1285
1286 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1287 while (node)
1288 {
1289 wxRichTextObject* child = node->GetData();
1290 long childEnd = 0;
1291
1292 child->CalculateRange(current, childEnd);
1293 lastEnd = childEnd;
1294
1295 current = childEnd + 1;
1296
1297 node = node->GetNext();
1298 }
1299
1300 if (IsTopLevel())
1301 {
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1304 end = start;
1305 m_range.SetRange(start, start);
1306
1307 // An object with no children has zero length
1308 if (m_children.GetCount() == 0)
1309 lastEnd --;
1310 m_ownRange.SetRange(0, lastEnd);
1311 }
1312 else
1313 {
1314 end = lastEnd;
1315
1316 // An object with no children has zero length
1317 if (m_children.GetCount() == 0)
1318 end --;
1319
1320 m_range.SetRange(start, end);
1321 }
1322 }
1323
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1326 {
1327 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1328
1329 while (node)
1330 {
1331 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1332 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1333
1334 // Delete the range in each paragraph
1335
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1342
1343 if (!obj->GetRange().IsOutside(range))
1344 {
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj->IsTopLevel())
1347 obj->DeleteRange(range);
1348
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj->IsEmpty() ||
1351 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1352 {
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj, true);
1356 }
1357 }
1358
1359 node = next;
1360 }
1361
1362 return true;
1363 }
1364
1365 /// Get any text in this object for the given range
1366 wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1367 {
1368 wxString text;
1369 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1370 while (node)
1371 {
1372 wxRichTextObject* child = node->GetData();
1373 wxRichTextRange childRange = range;
1374 if (!child->GetRange().IsOutside(range))
1375 {
1376 childRange.LimitTo(child->GetRange());
1377
1378 wxString childText = child->GetTextForRange(childRange);
1379
1380 text += childText;
1381 }
1382 node = node->GetNext();
1383 }
1384
1385 return text;
1386 }
1387
1388 /// Get the child object at the given character position
1389 wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1390 {
1391 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1392 while (node)
1393 {
1394 wxRichTextObject* child = node->GetData();
1395 if (child->GetRange().GetStart() == pos)
1396 return child;
1397 node = node->GetNext();
1398 }
1399 return NULL;
1400 }
1401
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(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 wxRichTextObjectList::compatibility_iterator nextNode = node->GetNext();
1451
1452 // First split child and nextChild so we have smaller fragments to merge.
1453 // Then Merge only has to test per-object virtual attributes
1454 // because for an object with all the same sub-object attributes,
1455 // then any general virtual attributes should be merged with sub-objects by
1456 // the implementation.
1457
1458 wxRichTextObject* nextChildAfterSplit = nextChild;
1459
1460 if (nextChildAfterSplit->CanSplit(context))
1461 nextChildAfterSplit = nextChild->Split(context);
1462
1463 bool splitNextChild = nextChild != nextChildAfterSplit;
1464
1465 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1466 // Note that we use nextChild because if we had split nextChild, the first object always
1467 // remains (and further parts are appended). However we must use childAfterSplit since
1468 // it's the last part of a possibly split child.
1469
1470 if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
1471 {
1472 nextChild->Dereference();
1473 m_children.Erase(node->GetNext());
1474
1475 // Don't set node -- we'll see if we can merge again with the next
1476 // child. UNLESS we split this or the next child, in which case we know we have to
1477 // move on to the end of the next child.
1478 if (splitNextChild)
1479 node = m_children.Find(nextChildAfterSplit);
1480 }
1481 else
1482 {
1483 if (splitNextChild)
1484 node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
1485 else
1486 node = node->GetNext();
1487 }
1488 }
1489 else
1490 node = node->GetNext();
1491 }
1492 }
1493 else
1494 node = node->GetNext();
1495 }
1496
1497 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1498 if (GetChildCount() > 1)
1499 {
1500 node = m_children.GetFirst();
1501 while (node)
1502 {
1503 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1504 wxRichTextObject* child = node->GetData();
1505 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1506 {
1507 if (child->IsEmpty())
1508 {
1509 child->Dereference();
1510 m_children.Erase(node);
1511 }
1512 node = next;
1513 }
1514 else
1515 node = node->GetNext();
1516 }
1517 }
1518
1519 return true;
1520 }
1521
1522 /// Dump to output stream for debugging
1523 void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
1524 {
1525 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1526 while (node)
1527 {
1528 wxRichTextObject* child = node->GetData();
1529 child->Dump(stream);
1530 node = node->GetNext();
1531 }
1532 }
1533
1534 /// Get/set the object size for the given range. Returns false if the range
1535 /// is invalid for this object.
1536 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
1537 {
1538 if (!range.IsWithin(GetRange()))
1539 return false;
1540
1541 wxSize sz;
1542
1543 wxArrayInt childExtents;
1544 wxArrayInt* p;
1545 if (partialExtents)
1546 p = & childExtents;
1547 else
1548 p = NULL;
1549
1550 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1551 while (node)
1552 {
1553 wxRichTextObject* child = node->GetData();
1554 if (!child->GetRange().IsOutside(range))
1555 {
1556 // Floating objects have a zero size within the paragraph.
1557 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1558 {
1559 if (partialExtents)
1560 {
1561 int lastSize;
1562 if (partialExtents->GetCount() > 0)
1563 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1564 else
1565 lastSize = 0;
1566
1567 partialExtents->Add(0 /* zero size */ + lastSize);
1568 }
1569 }
1570 else
1571 {
1572 wxSize childSize;
1573
1574 wxRichTextRange rangeToUse = range;
1575 rangeToUse.LimitTo(child->GetRange());
1576 if (child->IsTopLevel())
1577 rangeToUse = child->GetOwnRange();
1578
1579 int childDescent = 0;
1580
1581 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1582 // but it's only going to be used after caching has taken place.
1583 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1584 {
1585 childDescent = child->GetDescent();
1586 childSize = child->GetCachedSize();
1587
1588 sz.y = wxMax(sz.y, childSize.y);
1589 sz.x += childSize.x;
1590 descent = wxMax(descent, childDescent);
1591 }
1592 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
1593 {
1594 sz.y = wxMax(sz.y, childSize.y);
1595 sz.x += childSize.x;
1596 descent = wxMax(descent, childDescent);
1597
1598 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1599 {
1600 child->SetCachedSize(childSize);
1601 child->SetDescent(childDescent);
1602 }
1603
1604 if (partialExtents)
1605 {
1606 int lastSize;
1607 if (partialExtents->GetCount() > 0)
1608 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1609 else
1610 lastSize = 0;
1611
1612 size_t i;
1613 for (i = 0; i < childExtents.GetCount(); i++)
1614 {
1615 partialExtents->Add(childExtents[i] + lastSize);
1616 }
1617 }
1618 }
1619 }
1620
1621 if (p)
1622 p->Clear();
1623 }
1624
1625 node = node->GetNext();
1626 }
1627 size = sz;
1628 return true;
1629 }
1630
1631 // Invalidate the buffer. With no argument, invalidates whole buffer.
1632 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1633 {
1634 wxRichTextObject::Invalidate(invalidRange);
1635
1636 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1637 while (node)
1638 {
1639 wxRichTextObject* child = node->GetData();
1640 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1641 {
1642 // Skip
1643 }
1644 else if (child->IsTopLevel())
1645 {
1646 if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
1647 {
1648 // Don't invalidate subhierarchy if we've already been laid out
1649 }
1650 else
1651 {
1652 if (invalidRange == wxRICHTEXT_NONE)
1653 child->Invalidate(wxRICHTEXT_NONE);
1654 else
1655 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1656 }
1657 }
1658 else
1659 child->Invalidate(invalidRange);
1660 node = node->GetNext();
1661 }
1662 }
1663
1664 // Move the object recursively, by adding the offset from old to new
1665 void wxRichTextCompositeObject::Move(const wxPoint& pt)
1666 {
1667 wxPoint oldPos = GetPosition();
1668 SetPosition(pt);
1669 wxPoint offset = pt - oldPos;
1670
1671 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1672 while (node)
1673 {
1674 wxRichTextObject* child = node->GetData();
1675 wxPoint childPos = child->GetPosition() + offset;
1676 child->Move(childPos);
1677 node = node->GetNext();
1678 }
1679 }
1680
1681
1682 /*!
1683 * wxRichTextParagraphLayoutBox
1684 * This box knows how to lay out paragraphs.
1685 */
1686
1687 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1688
1689 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1690 wxRichTextCompositeObject(parent)
1691 {
1692 Init();
1693 }
1694
1695 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1696 {
1697 if (m_floatCollector)
1698 {
1699 delete m_floatCollector;
1700 m_floatCollector = NULL;
1701 }
1702 }
1703
1704 /// Initialize the object.
1705 void wxRichTextParagraphLayoutBox::Init()
1706 {
1707 m_ctrl = NULL;
1708
1709 // For now, assume is the only box and has no initial size.
1710 m_range = wxRichTextRange(0, -1);
1711 m_ownRange = wxRichTextRange(0, -1);
1712
1713 m_invalidRange = wxRICHTEXT_ALL;
1714
1715 m_partialParagraph = false;
1716 m_floatCollector = NULL;
1717 }
1718
1719 void wxRichTextParagraphLayoutBox::Clear()
1720 {
1721 DeleteChildren();
1722
1723 if (m_floatCollector)
1724 delete m_floatCollector;
1725 m_floatCollector = NULL;
1726 m_partialParagraph = false;
1727 }
1728
1729 /// Copy
1730 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1731 {
1732 Clear();
1733
1734 wxRichTextCompositeObject::Copy(obj);
1735
1736 m_partialParagraph = obj.m_partialParagraph;
1737 m_defaultAttributes = obj.m_defaultAttributes;
1738 }
1739
1740 // Gather information about floating objects; only gather floats for those paragraphs that
1741 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1742 // during layout.
1743 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
1744 {
1745 if (m_floatCollector != NULL)
1746 delete m_floatCollector;
1747 m_floatCollector = new wxRichTextFloatCollector(availableRect);
1748 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1749 // Only gather floats up to the point we'll start formatting paragraphs.
1750 while (untilObj && node && node->GetData() != untilObj)
1751 {
1752 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1753 wxASSERT (child != NULL);
1754 if (child)
1755 m_floatCollector->CollectFloat(child);
1756 node = node->GetNext();
1757 }
1758
1759 return true;
1760 }
1761
1762 // Returns the style sheet associated with the overall buffer.
1763 wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1764 {
1765 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1766 }
1767
1768 // Get the number of floating objects at this level
1769 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1770 {
1771 if (m_floatCollector)
1772 return m_floatCollector->GetFloatingObjectCount();
1773 else
1774 return 0;
1775 }
1776
1777 // Get a list of floating objects
1778 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1779 {
1780 if (m_floatCollector)
1781 {
1782 return m_floatCollector->GetFloatingObjects(objects);
1783 }
1784 else
1785 return false;
1786 }
1787
1788 // Calculate ranges
1789 void wxRichTextParagraphLayoutBox::UpdateRanges()
1790 {
1791 long start = 0;
1792 if (GetParent())
1793 start = GetRange().GetStart();
1794 long end;
1795 CalculateRange(start, end);
1796 }
1797
1798 // HitTest
1799 int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1800 {
1801 if (!IsShown())
1802 return wxRICHTEXT_HITTEST_NONE;
1803
1804 int ret = wxRICHTEXT_HITTEST_NONE;
1805 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
1806 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
1807
1808 if (ret == wxRICHTEXT_HITTEST_NONE)
1809 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1810 else
1811 {
1812 *contextObj = this;
1813 return ret;
1814 }
1815 }
1816
1817 /// Draw the floating objects
1818 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1819 {
1820 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
1821 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
1822 }
1823
1824 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
1825 {
1826 if (from == to)
1827 return;
1828
1829 from->RemoveChild(obj);
1830 to->AppendChild(obj);
1831 }
1832
1833 /// Draw the item
1834 bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1835 {
1836 if (!IsShown())
1837 return true;
1838
1839 wxRect thisRect(GetPosition(), GetCachedSize());
1840
1841 wxRichTextAttr attr(GetAttributes());
1842 context.ApplyVirtualAttributes(attr, this);
1843
1844 int flags = style;
1845 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1846 flags |= wxRICHTEXT_DRAW_SELECTED;
1847
1848 // Don't draw guidelines if at top level
1849 int theseFlags = flags;
1850 if (!GetParent())
1851 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
1852 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
1853
1854 if (wxRichTextBuffer::GetFloatingLayoutMode())
1855 DrawFloats(dc, context, range, selection, rect, descent, style);
1856
1857 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1858 while (node)
1859 {
1860 wxRichTextObject* child = node->GetData();
1861
1862 if (child && !child->GetRange().IsOutside(range))
1863 {
1864 wxRect childRect(child->GetPosition(), child->GetCachedSize());
1865 wxRichTextRange childRange = range;
1866 if (child->IsTopLevel())
1867 {
1868 childRange = child->GetOwnRange();
1869 }
1870
1871 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1872 {
1873 // Stop drawing
1874 break;
1875 }
1876 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
1877 {
1878 // Skip
1879 }
1880 else
1881 child->Draw(dc, context, childRange, selection, rect, descent, style);
1882 }
1883
1884 node = node->GetNext();
1885 }
1886 return true;
1887 }
1888
1889 /// Lay the item out
1890 bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
1891 {
1892 SetPosition(rect.GetPosition());
1893
1894 if (!IsShown())
1895 return true;
1896
1897 wxRect availableSpace;
1898 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1899
1900 wxRichTextAttr attr(GetAttributes());
1901 context.ApplyVirtualAttributes(attr, this);
1902
1903 // If only laying out a specific area, the passed rect has a different meaning:
1904 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1905 // so that during a size, only the visible part will be relaid out, or
1906 // it would take too long causing flicker. As an approximation, we assume that
1907 // everything up to the start of the visible area is laid out correctly.
1908 if (formatRect)
1909 {
1910 wxRect rect2(0, 0, rect.width, rect.height);
1911 availableSpace = GetAvailableContentArea(dc, context, rect2);
1912
1913 // Invalidate the part of the buffer from the first visible line
1914 // to the end. If other parts of the buffer are currently invalid,
1915 // then they too will be taken into account if they are above
1916 // the visible point.
1917 long startPos = 0;
1918 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1919 if (line)
1920 startPos = line->GetAbsoluteRange().GetStart();
1921
1922 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
1923 }
1924 else
1925 {
1926 availableSpace = GetAvailableContentArea(dc, context, rect);
1927 }
1928
1929 // Fix the width if we're at the top level
1930 if (!GetParent())
1931 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1932
1933 int leftMargin, rightMargin, topMargin, bottomMargin;
1934 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
1935 topMargin, bottomMargin);
1936
1937 int maxWidth = 0;
1938 int maxHeight = 0;
1939
1940 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1941 int maxMaxWidth = 0;
1942
1943 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1944 int maxMinWidth = 0;
1945
1946 // If we have vertical alignment, we must recalculate everything.
1947 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1948 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
1949
1950 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1951
1952 bool layoutAll = true;
1953
1954 // Get invalid range, rounding to paragraph start/end.
1955 wxRichTextRange invalidRange = GetInvalidRange(true);
1956
1957 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1958 return true;
1959
1960 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1961 layoutAll = true;
1962 else // If we know what range is affected, start laying out from that point on.
1963 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
1964 {
1965 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
1966 if (firstParagraph)
1967 {
1968 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
1969 wxRichTextObjectList::compatibility_iterator previousNode;
1970 if ( firstNode )
1971 previousNode = firstNode->GetPrevious();
1972 if (firstNode)
1973 {
1974 if (previousNode)
1975 {
1976 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1977 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1978 }
1979
1980 // Now we're going to start iterating from the first affected paragraph.
1981 node = firstNode;
1982
1983 layoutAll = false;
1984 }
1985 }
1986 }
1987
1988 // Gather information about only those floating objects that will not be formatted,
1989 // after which floats will be gathered per-paragraph during layout.
1990 if (wxRichTextBuffer::GetFloatingLayoutMode())
1991 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
1992
1993 // A way to force speedy rest-of-buffer layout (the 'else' below)
1994 bool forceQuickLayout = false;
1995
1996 // First get the size of the paragraphs we won't be laying out
1997 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1998 while (n && n != node)
1999 {
2000 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
2001 if (child)
2002 {
2003 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2004 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2005 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2006 }
2007 n = n->GetNext();
2008 }
2009
2010 while (node)
2011 {
2012 // Assume this box only contains paragraphs
2013
2014 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2015 // Unsure if this is needed
2016 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2017
2018 if (child && child->IsShown())
2019 {
2020 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2021 if ( !forceQuickLayout &&
2022 (layoutAll ||
2023 child->GetLines().IsEmpty() ||
2024 !child->GetRange().IsOutside(invalidRange)) )
2025 {
2026 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2027 // lays out the object again using the minimum size
2028 child->LayoutToBestSize(dc, context, GetBuffer(),
2029 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
2030
2031 // Layout must set the cached size
2032 availableSpace.y += child->GetCachedSize().y;
2033 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2034 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2035 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2036
2037 // If we're just formatting the visible part of the buffer,
2038 // and we're now past the bottom of the window, and we don't have any
2039 // floating objects (since they may cause wrapping to change for the rest of the
2040 // the buffer), start quick layout.
2041 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
2042 forceQuickLayout = true;
2043 }
2044 else
2045 {
2046 // We're outside the immediately affected range, so now let's just
2047 // move everything up or down. This assumes that all the children have previously
2048 // been laid out and have wrapped line lists associated with them.
2049 // TODO: check all paragraphs before the affected range.
2050
2051 int inc = availableSpace.y - child->GetPosition().y;
2052
2053 while (node)
2054 {
2055 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2056 if (child)
2057 {
2058 if (child->GetLines().GetCount() == 0)
2059 {
2060 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2061 // lays out the object again using the minimum size
2062 child->LayoutToBestSize(dc, context, GetBuffer(),
2063 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
2064
2065 //child->Layout(dc, availableChildRect, style);
2066 }
2067 else
2068 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
2069
2070 availableSpace.y += child->GetCachedSize().y;
2071 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2072 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2073 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2074 }
2075
2076 node = node->GetNext();
2077 }
2078 break;
2079 }
2080 }
2081
2082 node = node->GetNext();
2083 }
2084
2085 node = m_children.GetLast();
2086 if (node && node->GetData()->IsShown())
2087 {
2088 wxRichTextObject* child = node->GetData();
2089 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2090 }
2091 else
2092 maxHeight = 0; // topMargin + bottomMargin;
2093
2094 // Check the bottom edge of any floating object
2095 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2096 {
2097 int bottom = GetFloatCollector()->GetLastRectBottom();
2098 if (bottom > maxHeight)
2099 maxHeight = bottom;
2100 }
2101
2102 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2103 {
2104 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
2105 int w = r.GetWidth();
2106
2107 // Convert external to content rect
2108 w = w - leftMargin - rightMargin;
2109 maxWidth = wxMax(maxWidth, w);
2110 maxMaxWidth = wxMax(maxMaxWidth, w);
2111 }
2112 else
2113 {
2114 // TODO: Make sure the layout box's position reflects
2115 // the position of the children, but without
2116 // breaking layout of a box within a paragraph.
2117 }
2118
2119 // TODO: (also in para layout) should set the
2120 // object's size to an absolute one if specified,
2121 // but if not specified, calculate it from content.
2122
2123 // We need to add back the margins etc.
2124 {
2125 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2126 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
2127 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2128 SetCachedSize(marginRect.GetSize());
2129 }
2130
2131 // The maximum size is the greatest of all maximum widths for all paragraphs.
2132 {
2133 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2134 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
2135 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2136 SetMaxSize(marginRect.GetSize());
2137 }
2138
2139 // The minimum size is the greatest of all minimum widths for all paragraphs.
2140 {
2141 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2142 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
2143 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2144 SetMinSize(marginRect.GetSize());
2145 }
2146
2147 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2148 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
2149 {
2150 int yOffset = 0;
2151 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2152 if (leftOverSpace > 0)
2153 {
2154 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
2155 {
2156 yOffset = (leftOverSpace/2);
2157 }
2158 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
2159 {
2160 yOffset = leftOverSpace;
2161 }
2162 }
2163
2164 // Move all the children to vertically align the content
2165 // This doesn't take into account floating objects, unfortunately.
2166 if (yOffset != 0)
2167 {
2168 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2169 while (node)
2170 {
2171 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2172 if (child)
2173 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
2174
2175 node = node->GetNext();
2176 }
2177 }
2178 }
2179
2180 m_invalidRange = wxRICHTEXT_NONE;
2181
2182 return true;
2183 }
2184
2185 /// Get/set the size for the given range.
2186 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
2187 {
2188 wxSize sz;
2189
2190 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2191 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
2192
2193 // First find the first paragraph whose starting position is within the range.
2194 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2195 while (node)
2196 {
2197 // child is a paragraph
2198 wxRichTextObject* child = node->GetData();
2199 const wxRichTextRange& r = child->GetRange();
2200
2201 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2202 {
2203 startPara = node;
2204 break;
2205 }
2206
2207 node = node->GetNext();
2208 }
2209
2210 // Next find the last paragraph containing part of the range
2211 node = m_children.GetFirst();
2212 while (node)
2213 {
2214 // child is a paragraph
2215 wxRichTextObject* child = node->GetData();
2216 const wxRichTextRange& r = child->GetRange();
2217
2218 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2219 {
2220 endPara = node;
2221 break;
2222 }
2223
2224 node = node->GetNext();
2225 }
2226
2227 if (!startPara || !endPara)
2228 return false;
2229
2230 // Now we can add up the sizes
2231 for (node = startPara; node ; node = node->GetNext())
2232 {
2233 // child is a paragraph
2234 wxRichTextObject* child = node->GetData();
2235 const wxRichTextRange& childRange = child->GetRange();
2236 wxRichTextRange rangeToFind = range;
2237 rangeToFind.LimitTo(childRange);
2238
2239 if (child->IsTopLevel())
2240 rangeToFind = child->GetOwnRange();
2241
2242 wxSize childSize;
2243
2244 int childDescent = 0;
2245 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
2246
2247 descent = wxMax(childDescent, descent);
2248
2249 sz.x = wxMax(sz.x, childSize.x);
2250 sz.y += childSize.y;
2251
2252 if (node == endPara)
2253 break;
2254 }
2255
2256 size = sz;
2257
2258 return true;
2259 }
2260
2261 /// Get the paragraph at the given position
2262 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2263 {
2264 if (caretPosition)
2265 pos ++;
2266
2267 // First find the first paragraph whose starting position is within the range.
2268 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2269 while (node)
2270 {
2271 // child is a paragraph
2272 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2273 // wxASSERT (child != NULL);
2274
2275 if (child)
2276 {
2277 // Return first child in buffer if position is -1
2278 // if (pos == -1)
2279 // return child;
2280
2281 if (child->GetRange().Contains(pos))
2282 return child;
2283 }
2284
2285 node = node->GetNext();
2286 }
2287 return NULL;
2288 }
2289
2290 /// Get the line at the given position
2291 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2292 {
2293 if (caretPosition)
2294 pos ++;
2295
2296 // First find the first paragraph whose starting position is within the range.
2297 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2298 while (node)
2299 {
2300 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2301 if (obj->GetRange().Contains(pos))
2302 {
2303 // child is a paragraph
2304 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
2305 // wxASSERT (child != NULL);
2306
2307 if (child)
2308 {
2309 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2310 while (node2)
2311 {
2312 wxRichTextLine* line = node2->GetData();
2313
2314 wxRichTextRange range = line->GetAbsoluteRange();
2315
2316 if (range.Contains(pos) ||
2317
2318 // If the position is end-of-paragraph, then return the last line of
2319 // of the paragraph.
2320 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2321 return line;
2322
2323 node2 = node2->GetNext();
2324 }
2325 }
2326 }
2327
2328 node = node->GetNext();
2329 }
2330
2331 int lineCount = GetLineCount();
2332 if (lineCount > 0)
2333 return GetLineForVisibleLineNumber(lineCount-1);
2334 else
2335 return NULL;
2336 }
2337
2338 /// Get the line at the given y pixel position, or the last line.
2339 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2340 {
2341 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2342 while (node)
2343 {
2344 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2345 // wxASSERT (child != NULL);
2346
2347 if (child)
2348 {
2349 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2350 while (node2)
2351 {
2352 wxRichTextLine* line = node2->GetData();
2353
2354 wxRect rect(line->GetRect());
2355
2356 if (y <= rect.GetBottom())
2357 return line;
2358
2359 node2 = node2->GetNext();
2360 }
2361 }
2362
2363 node = node->GetNext();
2364 }
2365
2366 // Return last line
2367 int lineCount = GetLineCount();
2368 if (lineCount > 0)
2369 return GetLineForVisibleLineNumber(lineCount-1);
2370 else
2371 return NULL;
2372 }
2373
2374 /// Get the number of visible lines
2375 int wxRichTextParagraphLayoutBox::GetLineCount() const
2376 {
2377 int count = 0;
2378
2379 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2380 while (node)
2381 {
2382 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2383 // wxASSERT (child != NULL);
2384
2385 if (child)
2386 count += child->GetLines().GetCount();
2387
2388 node = node->GetNext();
2389 }
2390 return count;
2391 }
2392
2393
2394 /// Get the paragraph for a given line
2395 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2396 {
2397 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
2398 }
2399
2400 /// Get the line size at the given position
2401 wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2402 {
2403 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2404 if (line)
2405 {
2406 return line->GetSize();
2407 }
2408 else
2409 return wxSize(0, 0);
2410 }
2411
2412
2413 /// Convenience function to add a paragraph of text
2414 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
2415 {
2416 // Don't use the base style, just the default style, and the base style will
2417 // be combined at display time.
2418 // Divide into paragraph and character styles.
2419
2420 wxRichTextAttr defaultCharStyle;
2421 wxRichTextAttr defaultParaStyle;
2422
2423 // If the default style is a named paragraph style, don't apply any character formatting
2424 // to the initial text string.
2425 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2426 {
2427 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2428 if (def)
2429 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2430 }
2431 else
2432 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2433
2434 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2435 wxRichTextAttr* cStyle = & defaultCharStyle;
2436
2437 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
2438 para->GetAttributes().GetTextBoxAttr().Reset();
2439
2440 AppendChild(para);
2441
2442 UpdateRanges();
2443
2444 return para->GetRange();
2445 }
2446
2447 /// Adds multiple paragraphs, based on newlines.
2448 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
2449 {
2450 // Don't use the base style, just the default style, and the base style will
2451 // be combined at display time.
2452 // Divide into paragraph and character styles.
2453
2454 wxRichTextAttr defaultCharStyle;
2455 wxRichTextAttr defaultParaStyle;
2456
2457 // If the default style is a named paragraph style, don't apply any character formatting
2458 // to the initial text string.
2459 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2460 {
2461 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2462 if (def)
2463 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2464 }
2465 else
2466 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2467
2468 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2469 wxRichTextAttr* cStyle = & defaultCharStyle;
2470
2471 wxRichTextParagraph* firstPara = NULL;
2472 wxRichTextParagraph* lastPara = NULL;
2473
2474 wxRichTextRange range(-1, -1);
2475
2476 size_t i = 0;
2477 size_t len = text.length();
2478 wxString line;
2479 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2480 para->GetAttributes().GetTextBoxAttr().Reset();
2481
2482 AppendChild(para);
2483
2484 firstPara = para;
2485 lastPara = para;
2486
2487 while (i < len)
2488 {
2489 wxChar ch = text[i];
2490 if (ch == wxT('\n') || ch == wxT('\r'))
2491 {
2492 if (i != (len-1))
2493 {
2494 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2495 plainText->SetText(line);
2496
2497 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2498 para->GetAttributes().GetTextBoxAttr().Reset();
2499
2500 AppendChild(para);
2501
2502 lastPara = para;
2503 line = wxEmptyString;
2504 }
2505 }
2506 else
2507 line += ch;
2508
2509 i ++;
2510 }
2511
2512 if (!line.empty())
2513 {
2514 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2515 plainText->SetText(line);
2516 }
2517
2518 UpdateRanges();
2519
2520 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
2521 }
2522
2523 /// Convenience function to add an image
2524 wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
2525 {
2526 // Don't use the base style, just the default style, and the base style will
2527 // be combined at display time.
2528 // Divide into paragraph and character styles.
2529
2530 wxRichTextAttr defaultCharStyle;
2531 wxRichTextAttr defaultParaStyle;
2532
2533 // If the default style is a named paragraph style, don't apply any character formatting
2534 // to the initial text string.
2535 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2536 {
2537 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2538 if (def)
2539 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2540 }
2541 else
2542 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2543
2544 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2545 wxRichTextAttr* cStyle = & defaultCharStyle;
2546
2547 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2548 para->GetAttributes().GetTextBoxAttr().Reset();
2549 AppendChild(para);
2550 para->AppendChild(new wxRichTextImage(image, this, cStyle));
2551
2552 UpdateRanges();
2553
2554 return para->GetRange();
2555 }
2556
2557
2558 /// Insert fragment into this box at the given position. If partialParagraph is true,
2559 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2560 /// marker.
2561
2562 bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
2563 {
2564 // First, find the first paragraph whose starting position is within the range.
2565 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2566 if (para)
2567 {
2568 wxRichTextAttr originalAttr = para->GetAttributes();
2569
2570 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2571
2572 // Now split at this position, returning the object to insert the new
2573 // ones in front of.
2574 wxRichTextObject* nextObject = para->SplitAt(position);
2575
2576 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2577 // text, for example, so let's optimize.
2578
2579 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2580 {
2581 // Add the first para to this para...
2582 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2583 if (!firstParaNode)
2584 return false;
2585
2586 // Iterate through the fragment paragraph inserting the content into this paragraph.
2587 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2588 wxASSERT (firstPara != NULL);
2589
2590 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2591 while (objectNode)
2592 {
2593 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2594
2595 if (!nextObject)
2596 {
2597 // Append
2598 para->AppendChild(newObj);
2599 }
2600 else
2601 {
2602 // Insert before nextObject
2603 para->InsertChild(newObj, nextObject);
2604 }
2605
2606 objectNode = objectNode->GetNext();
2607 }
2608
2609 return true;
2610 }
2611 else
2612 {
2613 // Procedure for inserting a fragment consisting of a number of
2614 // paragraphs:
2615 //
2616 // 1. Remove and save the content that's after the insertion point, for adding
2617 // back once we've added the fragment.
2618 // 2. Add the content from the first fragment paragraph to the current
2619 // paragraph.
2620 // 3. Add remaining fragment paragraphs after the current paragraph.
2621 // 4. Add back the saved content from the first paragraph. If partialParagraph
2622 // is true, add it to the last paragraph added and not a new one.
2623
2624 // 1. Remove and save objects after split point.
2625 wxList savedObjects;
2626 if (nextObject)
2627 para->MoveToList(nextObject, savedObjects);
2628
2629 // 2. Add the content from the 1st fragment paragraph.
2630 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2631 if (!firstParaNode)
2632 return false;
2633
2634 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2635 wxASSERT(firstPara != NULL);
2636
2637 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2638 para->SetAttributes(firstPara->GetAttributes());
2639
2640 // Save empty paragraph attributes for appending later
2641 // These are character attributes deliberately set for a new paragraph. Without this,
2642 // we couldn't pass default attributes when appending a new paragraph.
2643 wxRichTextAttr emptyParagraphAttributes;
2644
2645 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2646
2647 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2648 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2649
2650 while (objectNode)
2651 {
2652 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2653
2654 // Append
2655 para->AppendChild(newObj);
2656
2657 objectNode = objectNode->GetNext();
2658 }
2659
2660 // 3. Add remaining fragment paragraphs after the current paragraph.
2661 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2662 wxRichTextObject* nextParagraph = NULL;
2663 if (nextParagraphNode)
2664 nextParagraph = nextParagraphNode->GetData();
2665
2666 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2667 wxRichTextParagraph* finalPara = para;
2668
2669 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2670
2671 // If there was only one paragraph, we need to insert a new one.
2672 while (i)
2673 {
2674 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2675 wxASSERT( para != NULL );
2676
2677 finalPara = (wxRichTextParagraph*) para->Clone();
2678
2679 if (nextParagraph)
2680 InsertChild(finalPara, nextParagraph);
2681 else
2682 AppendChild(finalPara);
2683
2684 i = i->GetNext();
2685 }
2686
2687 // If there was only one paragraph, or we have full paragraphs in our fragment,
2688 // we need to insert a new one.
2689 if (needExtraPara)
2690 {
2691 finalPara = new wxRichTextParagraph;
2692
2693 if (nextParagraph)
2694 InsertChild(finalPara, nextParagraph);
2695 else
2696 AppendChild(finalPara);
2697 }
2698
2699 // 4. Add back the remaining content.
2700 if (finalPara)
2701 {
2702 if (nextObject)
2703 finalPara->MoveFromList(savedObjects);
2704
2705 // Ensure there's at least one object
2706 if (finalPara->GetChildCount() == 0)
2707 {
2708 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2709 text->SetAttributes(emptyParagraphAttributes);
2710
2711 finalPara->AppendChild(text);
2712 }
2713 }
2714
2715 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2716 finalPara->SetAttributes(firstPara->GetAttributes());
2717 else if (finalPara && finalPara != para)
2718 finalPara->SetAttributes(originalAttr);
2719
2720 return true;
2721 }
2722 }
2723 else
2724 {
2725 // Append
2726 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2727 while (i)
2728 {
2729 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2730 wxASSERT( para != NULL );
2731
2732 AppendChild(para->Clone());
2733
2734 i = i->GetNext();
2735 }
2736
2737 return true;
2738 }
2739 }
2740
2741 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2742 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2743 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
2744 {
2745 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2746 while (i)
2747 {
2748 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2749 wxASSERT( para != NULL );
2750
2751 if (!para->GetRange().IsOutside(range))
2752 {
2753 fragment.AppendChild(para->Clone());
2754 }
2755 i = i->GetNext();
2756 }
2757
2758 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2759 if (!fragment.IsEmpty())
2760 {
2761 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2762 wxASSERT( firstPara != NULL );
2763
2764 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2765 wxASSERT( lastPara != NULL );
2766
2767 if (!firstPara || !lastPara)
2768 return false;
2769
2770 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2771
2772 long firstPos = firstPara->GetRange().GetStart();
2773
2774 // Adjust for renumbering from zero
2775 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2776
2777 long end;
2778 fragment.CalculateRange(0, end);
2779
2780 // Chop off the start of the paragraph
2781 if (topTailRange.GetStart() > 0)
2782 {
2783 wxRichTextRange r(0, topTailRange.GetStart()-1);
2784 firstPara->DeleteRange(r);
2785
2786 // Make sure the numbering is correct
2787 fragment.CalculateRange(0, end);
2788
2789 // Now, we've deleted some positions, so adjust the range
2790 // accordingly.
2791 topTailRange.SetStart(range.GetLength());
2792 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2793 }
2794 else
2795 {
2796 topTailRange.SetStart(range.GetLength());
2797 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2798 }
2799
2800 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
2801 {
2802 lastPara->DeleteRange(topTailRange);
2803
2804 // Make sure the numbering is correct
2805 long end;
2806 fragment.CalculateRange(0, end);
2807
2808 // We only have part of a paragraph at the end
2809 fragment.SetPartialParagraph(true);
2810 }
2811 else
2812 {
2813 // We have a partial paragraph (don't save last new paragraph marker)
2814 // or complete paragraph
2815 fragment.SetPartialParagraph(isFragment);
2816 }
2817 }
2818
2819 return true;
2820 }
2821
2822 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2823 /// starting from zero at the start of the buffer.
2824 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2825 {
2826 if (caretPosition)
2827 pos ++;
2828
2829 int lineCount = 0;
2830
2831 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2832 while (node)
2833 {
2834 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2835 // wxASSERT( child != NULL );
2836
2837 if (child)
2838 {
2839 if (child->GetRange().Contains(pos))
2840 {
2841 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2842 while (node2)
2843 {
2844 wxRichTextLine* line = node2->GetData();
2845 wxRichTextRange lineRange = line->GetAbsoluteRange();
2846
2847 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2848 {
2849 // If the caret is displayed at the end of the previous wrapped line,
2850 // we want to return the line it's _displayed_ at (not the actual line
2851 // containing the position).
2852 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2853 return lineCount - 1;
2854 else
2855 return lineCount;
2856 }
2857
2858 lineCount ++;
2859
2860 node2 = node2->GetNext();
2861 }
2862 // If we didn't find it in the lines, it must be
2863 // the last position of the paragraph. So return the last line.
2864 return lineCount-1;
2865 }
2866 else
2867 lineCount += child->GetLines().GetCount();
2868 }
2869
2870 node = node->GetNext();
2871 }
2872
2873 // Not found
2874 return -1;
2875 }
2876
2877 /// Given a line number, get the corresponding wxRichTextLine object.
2878 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2879 {
2880 int lineCount = 0;
2881
2882 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2883 while (node)
2884 {
2885 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2886 // wxASSERT(child != NULL);
2887
2888 if (child)
2889 {
2890 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
2891 {
2892 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2893 while (node2)
2894 {
2895 wxRichTextLine* line = node2->GetData();
2896
2897 if (lineCount == lineNumber)
2898 return line;
2899
2900 lineCount ++;
2901
2902 node2 = node2->GetNext();
2903 }
2904 }
2905 else
2906 lineCount += child->GetLines().GetCount();
2907 }
2908
2909 node = node->GetNext();
2910 }
2911
2912 // Didn't find it
2913 return NULL;
2914 }
2915
2916 /// Delete range from layout.
2917 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2918 {
2919 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2920
2921 wxRichTextParagraph* firstPara = NULL;
2922 while (node)
2923 {
2924 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2925 // wxASSERT (obj != NULL);
2926
2927 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
2928
2929 if (obj)
2930 {
2931 // Delete the range in each paragraph
2932
2933 if (!obj->GetRange().IsOutside(range))
2934 {
2935 // Deletes the content of this object within the given range
2936 obj->DeleteRange(range);
2937
2938 wxRichTextRange thisRange = obj->GetRange();
2939 wxRichTextAttr thisAttr = obj->GetAttributes();
2940
2941 // If the whole paragraph is within the range to delete,
2942 // delete the whole thing.
2943 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
2944 {
2945 // Delete the whole object
2946 RemoveChild(obj, true);
2947 obj = NULL;
2948 }
2949 else if (!firstPara)
2950 firstPara = obj;
2951
2952 // If the range includes the paragraph end, we need to join this
2953 // and the next paragraph.
2954 if (range.GetEnd() <= thisRange.GetEnd())
2955 {
2956 // We need to move the objects from the next paragraph
2957 // to this paragraph
2958
2959 wxRichTextParagraph* nextParagraph = NULL;
2960 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2961 nextParagraph = obj;
2962 else
2963 {
2964 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2965 if (next)
2966 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2967 }
2968
2969 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2970
2971 wxRichTextAttr nextParaAttr;
2972 if (applyFinalParagraphStyle)
2973 {
2974 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2975 // not the next one.
2976 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2977 nextParaAttr = thisAttr;
2978 else
2979 nextParaAttr = nextParagraph->GetAttributes();
2980 }
2981
2982 if (firstPara && nextParagraph && firstPara != nextParagraph)
2983 {
2984 // Move the objects to the previous para
2985 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
2986
2987 while (node1)
2988 {
2989 wxRichTextObject* obj1 = node1->GetData();
2990
2991 firstPara->AppendChild(obj1);
2992
2993 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2994 nextParagraph->GetChildren().Erase(node1);
2995
2996 node1 = next1;
2997 }
2998
2999 // Delete the paragraph
3000 RemoveChild(nextParagraph, true);
3001 }
3002
3003 // Avoid empty paragraphs
3004 if (firstPara && firstPara->GetChildren().GetCount() == 0)
3005 {
3006 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
3007 firstPara->AppendChild(text);
3008 }
3009
3010 if (applyFinalParagraphStyle)
3011 firstPara->SetAttributes(nextParaAttr);
3012
3013 return true;
3014 }
3015 }
3016 }
3017
3018 node = next;
3019 }
3020
3021 return true;
3022 }
3023
3024 /// Get any text in this object for the given range
3025 wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
3026 {
3027 int lineCount = 0;
3028 wxString text;
3029 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3030 while (node)
3031 {
3032 wxRichTextObject* child = node->GetData();
3033 if (!child->GetRange().IsOutside(range))
3034 {
3035 wxRichTextRange childRange = range;
3036 childRange.LimitTo(child->GetRange());
3037
3038 wxString childText = child->GetTextForRange(childRange);
3039
3040 text += childText;
3041
3042 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
3043 text += wxT("\n");
3044
3045 lineCount ++;
3046 }
3047 node = node->GetNext();
3048 }
3049
3050 return text;
3051 }
3052
3053 /// Get all the text
3054 wxString wxRichTextParagraphLayoutBox::GetText() const
3055 {
3056 return GetTextForRange(GetOwnRange());
3057 }
3058
3059 /// Get the paragraph by number
3060 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
3061 {
3062 if ((size_t) paragraphNumber >= GetChildCount())
3063 return NULL;
3064
3065 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
3066 }
3067
3068 /// Get the length of the paragraph
3069 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3070 {
3071 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3072 if (para)
3073 return para->GetRange().GetLength() - 1; // don't include newline
3074 else
3075 return 0;
3076 }
3077
3078 /// Get the text of the paragraph
3079 wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3080 {
3081 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3082 if (para)
3083 return para->GetTextForRange(para->GetRange());
3084 else
3085 return wxEmptyString;
3086 }
3087
3088 /// Convert zero-based line column and paragraph number to a position.
3089 long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3090 {
3091 wxRichTextParagraph* para = GetParagraphAtLine(y);
3092 if (para)
3093 {
3094 return para->GetRange().GetStart() + x;
3095 }
3096 else
3097 return -1;
3098 }
3099
3100 /// Convert zero-based position to line column and paragraph number
3101 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3102 {
3103 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3104 if (para)
3105 {
3106 int count = 0;
3107 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3108 while (node)
3109 {
3110 wxRichTextObject* child = node->GetData();
3111 if (child == para)
3112 break;
3113 count ++;
3114 node = node->GetNext();
3115 }
3116
3117 *y = count;
3118 *x = pos - para->GetRange().GetStart();
3119
3120 return true;
3121 }
3122 else
3123 return false;
3124 }
3125
3126 /// Get the leaf object in a paragraph at this position.
3127 /// Given a line number, get the corresponding wxRichTextLine object.
3128 wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3129 {
3130 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3131 if (para)
3132 {
3133 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
3134
3135 while (node)
3136 {
3137 wxRichTextObject* child = node->GetData();
3138 if (child->GetRange().Contains(position))
3139 return child;
3140
3141 node = node->GetNext();
3142 }
3143 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3144 return para->GetChildren().GetLast()->GetData();
3145 }
3146 return NULL;
3147 }
3148
3149 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3150 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
3151 {
3152 bool characterStyle = false;
3153 bool paragraphStyle = false;
3154
3155 if (style.IsCharacterStyle())
3156 characterStyle = true;
3157 if (style.IsParagraphStyle())
3158 paragraphStyle = true;
3159
3160 wxRichTextBuffer* buffer = GetBuffer();
3161
3162 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3163 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3164 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3165 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
3166 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3167 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
3168
3169 // Apply paragraph style first, if any
3170 wxRichTextAttr wholeStyle(style);
3171
3172 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
3173 {
3174 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
3175 if (def)
3176 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3177 }
3178
3179 // Limit the attributes to be set to the content to only character attributes.
3180 wxRichTextAttr characterAttributes(wholeStyle);
3181 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3182
3183 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
3184 {
3185 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
3186 if (def)
3187 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3188 }
3189
3190 // If we are associated with a control, make undoable; otherwise, apply immediately
3191 // to the data.
3192
3193 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3194
3195 wxRichTextAction* action = NULL;
3196
3197 if (haveControl && withUndo)
3198 {
3199 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3200 action->SetRange(range);
3201 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3202 }
3203
3204 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3205 while (node)
3206 {
3207 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3208 // wxASSERT (para != NULL);
3209
3210 if (para && para->GetChildCount() > 0)
3211 {
3212 // Stop searching if we're beyond the range of interest
3213 if (para->GetRange().GetStart() > range.GetEnd())
3214 break;
3215
3216 if (!para->GetRange().IsOutside(range))
3217 {
3218 // We'll be using a copy of the paragraph to make style changes,
3219 // not updating the buffer directly.
3220 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3221
3222 if (haveControl && withUndo)
3223 {
3224 newPara = new wxRichTextParagraph(*para);
3225 action->GetNewParagraphs().AppendChild(newPara);
3226
3227 // Also store the old ones for Undo
3228 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3229 }
3230 else
3231 newPara = para;
3232
3233 // If we're specifying paragraphs only, then we really mean character formatting
3234 // to be included in the paragraph style
3235 if ((paragraphStyle || parasOnly) && !charactersOnly)
3236 {
3237 if (removeStyle)
3238 {
3239 // Removes the given style from the paragraph
3240 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3241 }
3242 else if (resetExistingStyle)
3243 newPara->GetAttributes() = wholeStyle;
3244 else
3245 {
3246 if (applyMinimal)
3247 {
3248 // Only apply attributes that will make a difference to the combined
3249 // style as seen on the display
3250 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
3251 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3252 }
3253 else
3254 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
3255 }
3256 }
3257
3258 // When applying paragraph styles dynamically, don't change the text objects' attributes
3259 // since they will computed as needed. Only apply the character styling if it's _only_
3260 // character styling. This policy is subject to change and might be put under user control.
3261
3262 // Hm. we might well be applying a mix of paragraph and character styles, in which
3263 // case we _do_ want to apply character styles regardless of what para styles are set.
3264 // But if we're applying a paragraph style, which has some character attributes, but
3265 // we only want the paragraphs to hold this character style, then we _don't_ want to
3266 // apply the character style. So we need to be able to choose.
3267
3268 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
3269 {
3270 wxRichTextRange childRange(range);
3271 childRange.LimitTo(newPara->GetRange());
3272
3273 // Find the starting position and if necessary split it so
3274 // we can start applying a different style.
3275 // TODO: check that the style actually changes or is different
3276 // from style outside of range
3277 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3278 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3279
3280 if (childRange.GetStart() == newPara->GetRange().GetStart())
3281 firstObject = newPara->GetChildren().GetFirst()->GetData();
3282 else
3283 firstObject = newPara->SplitAt(range.GetStart());
3284
3285 // Increment by 1 because we're apply the style one _after_ the split point
3286 long splitPoint = childRange.GetEnd();
3287 if (splitPoint != newPara->GetRange().GetEnd())
3288 splitPoint ++;
3289
3290 // Find last object
3291 if (splitPoint == newPara->GetRange().GetEnd())
3292 lastObject = newPara->GetChildren().GetLast()->GetData();
3293 else
3294 // lastObject is set as a side-effect of splitting. It's
3295 // returned as the object before the new object.
3296 (void) newPara->SplitAt(splitPoint, & lastObject);
3297
3298 wxASSERT(firstObject != NULL);
3299 wxASSERT(lastObject != NULL);
3300
3301 if (!firstObject || !lastObject)
3302 continue;
3303
3304 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3305 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3306
3307 wxASSERT(firstNode);
3308 wxASSERT(lastNode);
3309
3310 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3311
3312 while (node2)
3313 {
3314 wxRichTextObject* child = node2->GetData();
3315
3316 if (removeStyle)
3317 {
3318 // Removes the given style from the paragraph
3319 wxRichTextRemoveStyle(child->GetAttributes(), style);
3320 }
3321 else if (resetExistingStyle)
3322 child->GetAttributes() = characterAttributes;
3323 else
3324 {
3325 if (applyMinimal)
3326 {
3327 // Only apply attributes that will make a difference to the combined
3328 // style as seen on the display
3329 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
3330 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3331 }
3332 else
3333 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
3334 }
3335
3336 if (node2 == lastNode)
3337 break;
3338
3339 node2 = node2->GetNext();
3340 }
3341 }
3342 }
3343 }
3344
3345 node = node->GetNext();
3346 }
3347
3348 // Do action, or delay it until end of batch.
3349 if (haveControl && withUndo)
3350 buffer->SubmitAction(action);
3351
3352 return true;
3353 }
3354
3355 // Just change the attributes for this single object.
3356 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
3357 {
3358 wxRichTextBuffer* buffer = GetBuffer();
3359 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
3360 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3361 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3362
3363 wxRichTextAction *action = NULL;
3364 wxRichTextAttr newAttr = obj->GetAttributes();
3365 if (resetExistingStyle)
3366 newAttr = textAttr;
3367 else
3368 newAttr.Apply(textAttr);
3369
3370 if (haveControl && withUndo)
3371 {
3372 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3373 action->SetRange(obj->GetRange().FromInternal());
3374 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3375 action->MakeObject(obj);
3376
3377 action->GetAttributes() = newAttr;
3378 }
3379 else
3380 obj->GetAttributes() = newAttr;
3381
3382 if (haveControl && withUndo)
3383 buffer->SubmitAction(action);
3384 }
3385
3386 /// Get the text attributes for this position.
3387 bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
3388 {
3389 return DoGetStyle(position, style, true);
3390 }
3391
3392 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
3393 {
3394 return DoGetStyle(position, style, false);
3395 }
3396
3397 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3398 /// context attributes.
3399 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
3400 {
3401 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
3402
3403 if (style.IsParagraphStyle())
3404 {
3405 obj = GetParagraphAtPosition(position);
3406 if (obj)
3407 {
3408 if (combineStyles)
3409 {
3410 // Start with the base style
3411 style = GetAttributes();
3412 style.GetTextBoxAttr().Reset();
3413
3414 // Apply the paragraph style
3415 wxRichTextApplyStyle(style, obj->GetAttributes());
3416 }
3417 else
3418 style = obj->GetAttributes();
3419
3420 return true;
3421 }
3422 }
3423 else
3424 {
3425 obj = GetLeafObjectAtPosition(position);
3426 if (obj)
3427 {
3428 if (combineStyles)
3429 {
3430 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3431 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3432 }
3433 else
3434 style = obj->GetAttributes();
3435
3436 return true;
3437 }
3438 }
3439 return false;
3440 }
3441
3442 static bool wxHasStyle(long flags, long style)
3443 {
3444 return (flags & style) != 0;
3445 }
3446
3447 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3448 /// content.
3449 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3450 {
3451 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3452
3453 return true;
3454 }
3455
3456 /// Get the combined style for a range - if any attribute is different within the range,
3457 /// that attribute is not present within the flags.
3458 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3459 /// nested.
3460 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
3461 {
3462 style = wxRichTextAttr();
3463
3464 wxRichTextAttr clashingAttrPara, clashingAttrChar;
3465 wxRichTextAttr absentAttrPara, absentAttrChar;
3466
3467 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3468 while (node)
3469 {
3470 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3471 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
3472 {
3473 if (para->GetChildren().GetCount() == 0)
3474 {
3475 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
3476
3477 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
3478 }
3479 else
3480 {
3481 wxRichTextRange paraRange(para->GetRange());
3482 paraRange.LimitTo(range);
3483
3484 // First collect paragraph attributes only
3485 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3486 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3487 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
3488
3489 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3490
3491 while (childNode)
3492 {
3493 wxRichTextObject* child = childNode->GetData();
3494 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3495 {
3496 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
3497
3498 // Now collect character attributes only
3499 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
3500
3501 CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
3502 }
3503
3504 childNode = childNode->GetNext();
3505 }
3506 }
3507 }
3508 node = node->GetNext();
3509 }
3510 return true;
3511 }
3512
3513 /// Set default style
3514 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3515 {
3516 m_defaultAttributes = style;
3517 return true;
3518 }
3519
3520 /// Test if this whole range has character attributes of the specified kind. If any
3521 /// of the attributes are different within the range, the test fails. You
3522 /// can use this to implement, for example, bold button updating. style must have
3523 /// flags indicating which attributes are of interest.
3524 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3525 {
3526 int foundCount = 0;
3527 int matchingCount = 0;
3528
3529 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3530 while (node)
3531 {
3532 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3533 // wxASSERT (para != NULL);
3534
3535 if (para)
3536 {
3537 // Stop searching if we're beyond the range of interest
3538 if (para->GetRange().GetStart() > range.GetEnd())
3539 return foundCount == matchingCount && foundCount != 0;
3540
3541 if (!para->GetRange().IsOutside(range))
3542 {
3543 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
3544
3545 while (node2)
3546 {
3547 wxRichTextObject* child = node2->GetData();
3548 // Allow for empty string if no buffer
3549 wxRichTextRange childRange = child->GetRange();
3550 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3551 childRange.SetEnd(childRange.GetEnd()+1);
3552
3553 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
3554 {
3555 foundCount ++;
3556 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
3557
3558 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
3559 matchingCount ++;
3560 }
3561
3562 node2 = node2->GetNext();
3563 }
3564 }
3565 }
3566
3567 node = node->GetNext();
3568 }
3569
3570 return foundCount == matchingCount && foundCount != 0;
3571 }
3572
3573 /// Test if this whole range has paragraph attributes of the specified kind. If any
3574 /// of the attributes are different within the range, the test fails. You
3575 /// can use this to implement, for example, centering button updating. style must have
3576 /// flags indicating which attributes are of interest.
3577 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3578 {
3579 int foundCount = 0;
3580 int matchingCount = 0;
3581
3582 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3583 while (node)
3584 {
3585 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3586 // wxASSERT (para != NULL);
3587
3588 if (para)
3589 {
3590 // Stop searching if we're beyond the range of interest
3591 if (para->GetRange().GetStart() > range.GetEnd())
3592 return foundCount == matchingCount && foundCount != 0;
3593
3594 if (!para->GetRange().IsOutside(range))
3595 {
3596 wxRichTextAttr textAttr = GetAttributes();
3597 // Apply the paragraph style
3598 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3599
3600 foundCount ++;
3601 if (textAttr.EqPartial(style, false /* strong test */))
3602 matchingCount ++;
3603 }
3604 }
3605
3606 node = node->GetNext();
3607 }
3608 return foundCount == matchingCount && foundCount != 0;
3609 }
3610
3611 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3612 {
3613 wxRichTextBuffer* buffer = GetBuffer();
3614 if (buffer && buffer->GetRichTextCtrl())
3615 buffer->GetRichTextCtrl()->PrepareContent(container);
3616 }
3617
3618 /// Set character or paragraph properties
3619 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3620 {
3621 wxRichTextBuffer* buffer = GetBuffer();
3622
3623 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3624 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3625 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3626 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3627 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3628
3629 // If we are associated with a control, make undoable; otherwise, apply immediately
3630 // to the data.
3631
3632 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3633
3634 wxRichTextAction* action = NULL;
3635
3636 if (haveControl && withUndo)
3637 {
3638 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3639 action->SetRange(range);
3640 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3641 }
3642
3643 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3644 while (node)
3645 {
3646 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3647 // wxASSERT (para != NULL);
3648
3649 if (para && para->GetChildCount() > 0)
3650 {
3651 // Stop searching if we're beyond the range of interest
3652 if (para->GetRange().GetStart() > range.GetEnd())
3653 break;
3654
3655 if (!para->GetRange().IsOutside(range))
3656 {
3657 // We'll be using a copy of the paragraph to make style changes,
3658 // not updating the buffer directly.
3659 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3660
3661 if (haveControl && withUndo)
3662 {
3663 newPara = new wxRichTextParagraph(*para);
3664 action->GetNewParagraphs().AppendChild(newPara);
3665
3666 // Also store the old ones for Undo
3667 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3668 }
3669 else
3670 newPara = para;
3671
3672 if (parasOnly)
3673 {
3674 if (removeProperties)
3675 {
3676 // Removes the given style from the paragraph
3677 // TODO
3678 newPara->GetProperties().RemoveProperties(properties);
3679 }
3680 else if (resetExistingProperties)
3681 newPara->GetProperties() = properties;
3682 else
3683 newPara->GetProperties().MergeProperties(properties);
3684 }
3685
3686 // When applying paragraph styles dynamically, don't change the text objects' attributes
3687 // since they will computed as needed. Only apply the character styling if it's _only_
3688 // character styling. This policy is subject to change and might be put under user control.
3689
3690 // Hm. we might well be applying a mix of paragraph and character styles, in which
3691 // case we _do_ want to apply character styles regardless of what para styles are set.
3692 // But if we're applying a paragraph style, which has some character attributes, but
3693 // we only want the paragraphs to hold this character style, then we _don't_ want to
3694 // apply the character style. So we need to be able to choose.
3695
3696 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3697 {
3698 wxRichTextRange childRange(range);
3699 childRange.LimitTo(newPara->GetRange());
3700
3701 // Find the starting position and if necessary split it so
3702 // we can start applying different properties.
3703 // TODO: check that the properties actually change or are different
3704 // from properties outside of range
3705 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3706 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3707
3708 if (childRange.GetStart() == newPara->GetRange().GetStart())
3709 firstObject = newPara->GetChildren().GetFirst()->GetData();
3710 else
3711 firstObject = newPara->SplitAt(range.GetStart());
3712
3713 // Increment by 1 because we're apply the style one _after_ the split point
3714 long splitPoint = childRange.GetEnd();
3715 if (splitPoint != newPara->GetRange().GetEnd())
3716 splitPoint ++;
3717
3718 // Find last object
3719 if (splitPoint == newPara->GetRange().GetEnd())
3720 lastObject = newPara->GetChildren().GetLast()->GetData();
3721 else
3722 // lastObject is set as a side-effect of splitting. It's
3723 // returned as the object before the new object.
3724 (void) newPara->SplitAt(splitPoint, & lastObject);
3725
3726 wxASSERT(firstObject != NULL);
3727 wxASSERT(lastObject != NULL);
3728
3729 if (!firstObject || !lastObject)
3730 continue;
3731
3732 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3733 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3734
3735 wxASSERT(firstNode);
3736 wxASSERT(lastNode);
3737
3738 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3739
3740 while (node2)
3741 {
3742 wxRichTextObject* child = node2->GetData();
3743
3744 if (removeProperties)
3745 {
3746 // Removes the given properties from the paragraph
3747 child->GetProperties().RemoveProperties(properties);
3748 }
3749 else if (resetExistingProperties)
3750 child->GetProperties() = properties;
3751 else
3752 {
3753 child->GetProperties().MergeProperties(properties);
3754 }
3755
3756 if (node2 == lastNode)
3757 break;
3758
3759 node2 = node2->GetNext();
3760 }
3761 }
3762 }
3763 }
3764
3765 node = node->GetNext();
3766 }
3767
3768 // Do action, or delay it until end of batch.
3769 if (haveControl && withUndo)
3770 buffer->SubmitAction(action);
3771
3772 return true;
3773 }
3774
3775 void wxRichTextParagraphLayoutBox::Reset()
3776 {
3777 Clear();
3778
3779 wxRichTextBuffer* buffer = GetBuffer();
3780 if (buffer && buffer->GetRichTextCtrl())
3781 {
3782 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3783 event.SetEventObject(buffer->GetRichTextCtrl());
3784 event.SetContainer(this);
3785
3786 buffer->SendEvent(event, true);
3787 }
3788
3789 AddParagraph(wxEmptyString);
3790
3791 PrepareContent(*this);
3792
3793 InvalidateHierarchy(wxRICHTEXT_ALL);
3794 }
3795
3796 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3797 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3798 {
3799 wxRichTextCompositeObject::Invalidate(invalidRange);
3800
3801 DoInvalidate(invalidRange);
3802 }
3803
3804 // Do the (in)validation for this object only
3805 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3806 {
3807 if (invalidRange == wxRICHTEXT_ALL)
3808 {
3809 m_invalidRange = wxRICHTEXT_ALL;
3810 }
3811 // Already invalidating everything
3812 else if (m_invalidRange == wxRICHTEXT_ALL)
3813 {
3814 }
3815 else
3816 {
3817 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3818 m_invalidRange.SetStart(invalidRange.GetStart());
3819 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3820 m_invalidRange.SetEnd(invalidRange.GetEnd());
3821 }
3822 }
3823
3824 // Do the (in)validation both up and down the hierarchy
3825 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3826 {
3827 Invalidate(invalidRange);
3828
3829 if (invalidRange != wxRICHTEXT_NONE)
3830 {
3831 // Now go up the hierarchy
3832 wxRichTextObject* thisObj = this;
3833 wxRichTextObject* p = GetParent();
3834 while (p)
3835 {
3836 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3837 if (l)
3838 l->DoInvalidate(thisObj->GetRange());
3839
3840 thisObj = p;
3841 p = p->GetParent();
3842 }
3843 }
3844 }
3845
3846 /// Get invalid range, rounding to entire paragraphs if argument is true.
3847 wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3848 {
3849 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
3850 return m_invalidRange;
3851
3852 wxRichTextRange range = m_invalidRange;
3853
3854 if (wholeParagraphs)
3855 {
3856 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
3857 if (para1)
3858 range.SetStart(para1->GetRange().GetStart());
3859
3860 // FIXME: be more intelligent about this. Check if we have floating objects
3861 // before the end of the range. But it's not clear how we can in general
3862 // tell where it's safe to stop laying out.
3863 // Anyway, this code is central to efficiency when laying in floating mode.
3864 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3865 {
3866 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
3867 if (para2)
3868 range.SetEnd(para2->GetRange().GetEnd());
3869 }
3870 else
3871 // Floating layout means that all children should be laid out,
3872 // because we can't tell how the whole buffer will be affected.
3873 range.SetEnd(GetOwnRange().GetEnd());
3874 }
3875 return range;
3876 }
3877
3878 /// Apply the style sheet to the buffer, for example if the styles have changed.
3879 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3880 {
3881 wxASSERT(styleSheet != NULL);
3882 if (!styleSheet)
3883 return false;
3884
3885 int foundCount = 0;
3886
3887 wxRichTextAttr attr(GetBasicStyle());
3888 if (GetBasicStyle().HasParagraphStyleName())
3889 {
3890 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3891 if (paraDef)
3892 {
3893 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3894 SetBasicStyle(attr);
3895 foundCount ++;
3896 }
3897 }
3898
3899 if (GetBasicStyle().HasCharacterStyleName())
3900 {
3901 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3902 if (charDef)
3903 {
3904 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3905 SetBasicStyle(attr);
3906 foundCount ++;
3907 }
3908 }
3909
3910 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3911 while (node)
3912 {
3913 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3914 // wxASSERT (para != NULL);
3915
3916 if (para)
3917 {
3918 // Combine paragraph and list styles. If there is a list style in the original attributes,
3919 // the current indentation overrides anything else and is used to find the item indentation.
3920 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3921 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3922 // exception as above).
3923 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3924 // So when changing a list style interactively, could retrieve level based on current style, then
3925 // set appropriate indent and apply new style.
3926
3927 int outline = -1;
3928 int num = -1;
3929 if (para->GetAttributes().HasOutlineLevel())
3930 outline = para->GetAttributes().GetOutlineLevel();
3931 if (para->GetAttributes().HasBulletNumber())
3932 num = para->GetAttributes().GetBulletNumber();
3933
3934 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3935 {
3936 int currentIndent = para->GetAttributes().GetLeftIndent();
3937
3938 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3939 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3940 if (paraDef && !listDef)
3941 {
3942 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
3943 foundCount ++;
3944 }
3945 else if (listDef && !paraDef)
3946 {
3947 // Set overall style defined for the list style definition
3948 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3949
3950 // Apply the style for this level
3951 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3952 foundCount ++;
3953 }
3954 else if (listDef && paraDef)
3955 {
3956 // Combines overall list style, style for level, and paragraph style
3957 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
3958 foundCount ++;
3959 }
3960 }
3961 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3962 {
3963 int currentIndent = para->GetAttributes().GetLeftIndent();
3964
3965 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3966
3967 // Overall list definition style
3968 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3969
3970 // Style for this level
3971 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3972
3973 foundCount ++;
3974 }
3975 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
3976 {
3977 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3978 if (def)
3979 {
3980 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
3981 foundCount ++;
3982 }
3983 }
3984
3985 if (outline != -1)
3986 para->GetAttributes().SetOutlineLevel(outline);
3987 if (num != -1)
3988 para->GetAttributes().SetBulletNumber(num);
3989 }
3990
3991 node = node->GetNext();
3992 }
3993 return foundCount != 0;
3994 }
3995
3996 /// Set list style
3997 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3998 {
3999 wxRichTextBuffer* buffer = GetBuffer();
4000 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4001
4002 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4003 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4004 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4005 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4006
4007 // Current number, if numbering
4008 int n = startFrom;
4009
4010 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4011
4012 // If we are associated with a control, make undoable; otherwise, apply immediately
4013 // to the data.
4014
4015 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4016
4017 wxRichTextAction* action = NULL;
4018
4019 if (haveControl && withUndo)
4020 {
4021 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4022 action->SetRange(range);
4023 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4024 }
4025
4026 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4027 while (node)
4028 {
4029 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4030 // wxASSERT (para != NULL);
4031
4032 if (para && para->GetChildCount() > 0)
4033 {
4034 // Stop searching if we're beyond the range of interest
4035 if (para->GetRange().GetStart() > range.GetEnd())
4036 break;
4037
4038 if (!para->GetRange().IsOutside(range))
4039 {
4040 // We'll be using a copy of the paragraph to make style changes,
4041 // not updating the buffer directly.
4042 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4043
4044 if (haveControl && withUndo)
4045 {
4046 newPara = new wxRichTextParagraph(*para);
4047 action->GetNewParagraphs().AppendChild(newPara);
4048
4049 // Also store the old ones for Undo
4050 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4051 }
4052 else
4053 newPara = para;
4054
4055 if (def)
4056 {
4057 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4058 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
4059
4060 // How is numbering going to work?
4061 // If we are renumbering, or numbering for the first time, we need to keep
4062 // track of the number for each level. But we might be simply applying a different
4063 // list style.
4064 // In Word, applying a style to several paragraphs, even if at different levels,
4065 // reverts the level back to the same one. So we could do the same here.
4066 // Renumbering will need to be done when we promote/demote a paragraph.
4067
4068 // Apply the overall list style, and item style for this level
4069 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
4070 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4071
4072 // Now we need to do numbering
4073 // Preserve the existing list item continuation bullet style, if any
4074 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4075 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4076 else
4077 {
4078 if (renumber)
4079 {
4080 newPara->GetAttributes().SetBulletNumber(n);
4081 }
4082
4083 n ++;
4084 }
4085 }
4086 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4087 {
4088 // if def is NULL, remove list style, applying any associated paragraph style
4089 // to restore the attributes
4090
4091 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4092 newPara->GetAttributes().SetLeftIndent(0, 0);
4093 newPara->GetAttributes().SetBulletText(wxEmptyString);
4094 newPara->GetAttributes().SetBulletStyle(0);
4095
4096 // Eliminate the main list-related attributes
4097 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);
4098
4099 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4100 {
4101 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4102 if (def)
4103 {
4104 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
4105 }
4106 }
4107 }
4108 }
4109 }
4110
4111 node = node->GetNext();
4112 }
4113
4114 // Do action, or delay it until end of batch.
4115 if (haveControl && withUndo)
4116 buffer->SubmitAction(action);
4117
4118 return true;
4119 }
4120
4121 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4122 {
4123 wxRichTextBuffer* buffer = GetBuffer();
4124 if (buffer && buffer->GetStyleSheet())
4125 {
4126 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
4127 if (def)
4128 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4129 }
4130 return false;
4131 }
4132
4133 /// Clear list for given range
4134 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4135 {
4136 return SetListStyle(range, NULL, flags);
4137 }
4138
4139 /// Number/renumber any list elements in the given range
4140 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4141 {
4142 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4143 }
4144
4145 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4146 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4147 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4148 {
4149 wxRichTextBuffer* buffer = GetBuffer();
4150 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4151
4152 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4153 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4154 #if wxDEBUG_LEVEL
4155 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4156 #endif
4157
4158 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4159
4160 // Max number of levels
4161 const int maxLevels = 10;
4162
4163 // The level we're looking at now
4164 int currentLevel = -1;
4165
4166 // The item number for each level
4167 int levels[maxLevels];
4168 int i;
4169
4170 // Reset all numbering
4171 for (i = 0; i < maxLevels; i++)
4172 {
4173 if (startFrom != -1)
4174 levels[i] = startFrom-1;
4175 else if (renumber) // start again
4176 levels[i] = 0;
4177 else
4178 levels[i] = -1; // start from the number we found, if any
4179 }
4180
4181 #if wxDEBUG_LEVEL
4182 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4183 #endif
4184
4185 // If we are associated with a control, make undoable; otherwise, apply immediately
4186 // to the data.
4187
4188 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4189
4190 wxRichTextAction* action = NULL;
4191
4192 if (haveControl && withUndo)
4193 {
4194 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4195 action->SetRange(range);
4196 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4197 }
4198
4199 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4200 while (node)
4201 {
4202 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4203 // wxASSERT (para != NULL);
4204
4205 if (para && para->GetChildCount() > 0)
4206 {
4207 // Stop searching if we're beyond the range of interest
4208 if (para->GetRange().GetStart() > range.GetEnd())
4209 break;
4210
4211 if (!para->GetRange().IsOutside(range))
4212 {
4213 // We'll be using a copy of the paragraph to make style changes,
4214 // not updating the buffer directly.
4215 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4216
4217 if (haveControl && withUndo)
4218 {
4219 newPara = new wxRichTextParagraph(*para);
4220 action->GetNewParagraphs().AppendChild(newPara);
4221
4222 // Also store the old ones for Undo
4223 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4224 }
4225 else
4226 newPara = para;
4227
4228 wxRichTextListStyleDefinition* defToUse = def;
4229 if (!defToUse)
4230 {
4231 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4232 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
4233 }
4234
4235 if (defToUse)
4236 {
4237 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4238 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4239
4240 // If we've specified a level to apply to all, change the level.
4241 if (specifiedLevel != -1)
4242 thisLevel = specifiedLevel;
4243
4244 // Do promotion if specified
4245 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4246 {
4247 thisLevel = thisLevel - promoteBy;
4248 if (thisLevel < 0)
4249 thisLevel = 0;
4250 if (thisLevel > 9)
4251 thisLevel = 9;
4252 }
4253
4254 // Apply the overall list style, and item style for this level
4255 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
4256 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4257
4258 // Preserve the existing list item continuation bullet style, if any
4259 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4260 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4261
4262 // OK, we've (re)applied the style, now let's get the numbering right.
4263
4264 if (currentLevel == -1)
4265 currentLevel = thisLevel;
4266
4267 // Same level as before, do nothing except increment level's number afterwards
4268 if (currentLevel == thisLevel)
4269 {
4270 }
4271 // A deeper level: start renumbering all levels after current level
4272 else if (thisLevel > currentLevel)
4273 {
4274 for (i = currentLevel+1; i <= thisLevel; i++)
4275 {
4276 levels[i] = 0;
4277 }
4278 currentLevel = thisLevel;
4279 }
4280 else if (thisLevel < currentLevel)
4281 {
4282 currentLevel = thisLevel;
4283 }
4284
4285 // Use the current numbering if -1 and we have a bullet number already
4286 if (levels[currentLevel] == -1)
4287 {
4288 if (newPara->GetAttributes().HasBulletNumber())
4289 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4290 else
4291 levels[currentLevel] = 1;
4292 }
4293 else
4294 {
4295 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4296 levels[currentLevel] ++;
4297 }
4298
4299 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4300
4301 // Create the bullet text if an outline list
4302 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4303 {
4304 wxString text;
4305 for (i = 0; i <= currentLevel; i++)
4306 {
4307 if (!text.IsEmpty())
4308 text += wxT(".");
4309 text += wxString::Format(wxT("%d"), levels[i]);
4310 }
4311 newPara->GetAttributes().SetBulletText(text);
4312 }
4313 }
4314 }
4315 }
4316
4317 node = node->GetNext();
4318 }
4319
4320 // Do action, or delay it until end of batch.
4321 if (haveControl && withUndo)
4322 buffer->SubmitAction(action);
4323
4324 return true;
4325 }
4326
4327 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4328 {
4329 wxRichTextBuffer* buffer = GetBuffer();
4330 if (buffer->GetStyleSheet())
4331 {
4332 wxRichTextListStyleDefinition* def = NULL;
4333 if (!defName.IsEmpty())
4334 def = buffer->GetStyleSheet()->FindListStyle(defName);
4335 return NumberList(range, def, flags, startFrom, specifiedLevel);
4336 }
4337 return false;
4338 }
4339
4340 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4341 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4342 {
4343 // TODO
4344 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4345 // to NumberList with a flag indicating promotion is required within one of the ranges.
4346 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4347 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4348 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4349 // list position will start from 1.
4350 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4351 // We can end the renumbering at this point.
4352
4353 // For now, only renumber within the promotion range.
4354
4355 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4356 }
4357
4358 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4359 {
4360 wxRichTextBuffer* buffer = GetBuffer();
4361 if (buffer->GetStyleSheet())
4362 {
4363 wxRichTextListStyleDefinition* def = NULL;
4364 if (!defName.IsEmpty())
4365 def = buffer->GetStyleSheet()->FindListStyle(defName);
4366 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4367 }
4368 return false;
4369 }
4370
4371 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4372 /// position of the paragraph that it had to start looking from.
4373 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
4374 {
4375 // TODO: add GetNextChild/GetPreviousChild to composite
4376 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4377 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4378 {
4379 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4380 if (node)
4381 {
4382 node = node->GetPrevious();
4383 if (node)
4384 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4385 else
4386 previousParagraph = NULL;
4387 }
4388 else
4389 previousParagraph = NULL;
4390 }
4391
4392 if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4393 return false;
4394
4395 wxRichTextBuffer* buffer = GetBuffer();
4396 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4397 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
4398 {
4399 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
4400 if (def)
4401 {
4402 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4403 // int thisLevel = def->FindLevelForIndent(thisIndent);
4404
4405 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4406
4407 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4408 if (previousParagraph->GetAttributes().HasBulletName())
4409 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4410 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4411 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
4412
4413 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4414 attr.SetBulletNumber(nextNumber);
4415
4416 if (isOutline)
4417 {
4418 wxString text = previousParagraph->GetAttributes().GetBulletText();
4419 if (!text.IsEmpty())
4420 {
4421 int pos = text.Find(wxT('.'), true);
4422 if (pos != wxNOT_FOUND)
4423 {
4424 text = text.Mid(0, text.Length() - pos - 1);
4425 }
4426 else
4427 text = wxEmptyString;
4428 if (!text.IsEmpty())
4429 text += wxT(".");
4430 text += wxString::Format(wxT("%d"), nextNumber);
4431 attr.SetBulletText(text);
4432 }
4433 }
4434
4435 return true;
4436 }
4437 else
4438 return false;
4439 }
4440 else
4441 return false;
4442 }
4443
4444 /*!
4445 * wxRichTextParagraph
4446 * This object represents a single paragraph (or in a straight text editor, a line).
4447 */
4448
4449 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
4450
4451 wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4452
4453 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
4454 wxRichTextCompositeObject(parent)
4455 {
4456 if (style)
4457 SetAttributes(*style);
4458 }
4459
4460 wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
4461 wxRichTextCompositeObject(parent)
4462 {
4463 if (paraStyle)
4464 SetAttributes(*paraStyle);
4465
4466 AppendChild(new wxRichTextPlainText(text, this, charStyle));
4467 }
4468
4469 wxRichTextParagraph::~wxRichTextParagraph()
4470 {
4471 ClearLines();
4472 }
4473
4474 /// Draw the item
4475 bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
4476 {
4477 if (!IsShown())
4478 return true;
4479
4480 // Currently we don't merge these attributes with the parent, but we
4481 // should consider whether we should (e.g. if we set a border colour
4482 // for all paragraphs). But generally box attributes are likely to be
4483 // different for different objects.
4484 wxRect paraRect = GetRect();
4485 wxRichTextAttr attr = GetCombinedAttributes();
4486 context.ApplyVirtualAttributes(attr, this);
4487
4488 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
4489
4490 // Draw the bullet, if any
4491 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
4492 {
4493 if (attr.GetLeftSubIndent() != 0)
4494 {
4495 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4496 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4497
4498 wxRichTextAttr bulletAttr(attr);
4499
4500 // Combine with the font of the first piece of content, if one is specified
4501 if (GetChildren().GetCount() > 0)
4502 {
4503 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
4504 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
4505 {
4506 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4507 }
4508 }
4509
4510 // Get line height from first line, if any
4511 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
4512
4513 wxPoint linePos;
4514 int lineHeight wxDUMMY_INITIALIZE(0);
4515 if (line)
4516 {
4517 lineHeight = line->GetSize().y;
4518 linePos = line->GetPosition() + GetPosition();
4519 }
4520 else
4521 {
4522 wxFont font;
4523 if (bulletAttr.HasFont() && GetBuffer())
4524 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
4525 else
4526 font = (*wxNORMAL_FONT);
4527
4528 wxCheckSetFont(dc, font);
4529
4530 lineHeight = dc.GetCharHeight();
4531 linePos = GetPosition();
4532 linePos.y += spaceBeforePara;
4533 }
4534
4535 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
4536
4537 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4538 {
4539 if (wxRichTextBuffer::GetRenderer())
4540 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4541 }
4542 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4543 {
4544 if (wxRichTextBuffer::GetRenderer())
4545 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
4546 }
4547 else
4548 {
4549 wxString bulletText = GetBulletText();
4550
4551 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4552 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
4553 }
4554 }
4555 }
4556
4557 // Draw the range for each line, one object at a time.
4558
4559 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4560 while (node)
4561 {
4562 wxRichTextLine* line = node->GetData();
4563 wxRichTextRange lineRange = line->GetAbsoluteRange();
4564
4565 // Lines are specified relative to the paragraph
4566
4567 wxPoint linePosition = line->GetPosition() + GetPosition();
4568
4569 // Don't draw if off the screen
4570 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
4571 {
4572 wxPoint objectPosition = linePosition;
4573 int maxDescent = line->GetDescent();
4574
4575 // Loop through objects until we get to the one within range
4576 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4577
4578 int i = 0;
4579 while (node2)
4580 {
4581 wxRichTextObject* child = node2->GetData();
4582
4583 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
4584 {
4585 // Draw this part of the line at the correct position
4586 wxRichTextRange objectRange(child->GetRange());
4587 objectRange.LimitTo(lineRange);
4588
4589 wxSize objectSize;
4590 if (child->IsTopLevel())
4591 {
4592 objectSize = child->GetCachedSize();
4593 objectRange = child->GetOwnRange();
4594 }
4595 else
4596 {
4597 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4598 if (i < (int) line->GetObjectSizes().GetCount())
4599 {
4600 objectSize.x = line->GetObjectSizes()[(size_t) i];
4601 }
4602 else
4603 #endif
4604 {
4605 int descent = 0;
4606 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
4607 }
4608 }
4609
4610 // Use the child object's width, but the whole line's height
4611 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
4612 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
4613
4614 objectPosition.x += objectSize.x;
4615 i ++;
4616 }
4617 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4618 // Can break out of inner loop now since we've passed this line's range
4619 break;
4620
4621 node2 = node2->GetNext();
4622 }
4623 }
4624
4625 node = node->GetNext();
4626 }
4627
4628 return true;
4629 }
4630
4631 // Get the range width using partial extents calculated for the whole paragraph.
4632 static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4633 {
4634 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4635
4636 if (partialExtents.GetCount() < (size_t) range.GetLength())
4637 return 0;
4638
4639 int leftMostPos = 0;
4640 if (range.GetStart() - para.GetRange().GetStart() > 0)
4641 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4642
4643 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4644
4645 int w = rightMostPos - leftMostPos;
4646
4647 return w;
4648 }
4649
4650 /// Lay the item out
4651 bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
4652 {
4653 // Deal with floating objects firstly before the normal layout
4654 wxRichTextBuffer* buffer = GetBuffer();
4655 wxASSERT(buffer);
4656
4657 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
4658
4659 if (wxRichTextBuffer::GetFloatingLayoutMode())
4660 {
4661 wxASSERT(collector != NULL);
4662 if (collector)
4663 LayoutFloat(dc, context, rect, parentRect, style, collector);
4664 }
4665
4666 wxRichTextAttr attr = GetCombinedAttributes();
4667 context.ApplyVirtualAttributes(attr, this);
4668
4669 // ClearLines();
4670
4671 // Increase the size of the paragraph due to spacing
4672 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4673 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4674 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4675 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4676 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4677
4678 int lineSpacing = 0;
4679
4680 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4681 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
4682 {
4683 wxCheckSetFont(dc, attr.GetFont());
4684 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4685 }
4686
4687 // Start position for each line relative to the paragraph
4688 int startPositionFirstLine = leftIndent;
4689 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4690
4691 // If we have a bullet in this paragraph, the start position for the first line's text
4692 // is actually leftIndent + leftSubIndent.
4693 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
4694 startPositionFirstLine = startPositionSubsequentLines;
4695
4696 long lastEndPos = GetRange().GetStart()-1;
4697 long lastCompletedEndPos = lastEndPos;
4698
4699 int currentWidth = 0;
4700 SetPosition(rect.GetPosition());
4701
4702 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4703 int lineHeight = 0;
4704 int maxWidth = 0;
4705 int maxHeight = currentPosition.y;
4706 int maxAscent = 0;
4707 int maxDescent = 0;
4708 int lineCount = 0;
4709 int lineAscent = 0;
4710 int lineDescent = 0;
4711
4712 wxRichTextObjectList::compatibility_iterator node;
4713
4714 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4715 wxUnusedVar(style);
4716 wxArrayInt partialExtents;
4717
4718 wxSize paraSize;
4719 int paraDescent = 0;
4720
4721 // This calculates the partial text extents
4722 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
4723 #else
4724 node = m_children.GetFirst();
4725 while (node)
4726 {
4727 wxRichTextObject* child = node->GetData();
4728
4729 //child->SetCachedSize(wxDefaultSize);
4730 child->Layout(dc, context, rect, style);
4731
4732 node = node->GetNext();
4733 }
4734 #endif
4735
4736 // Split up lines
4737
4738 // We may need to go back to a previous child, in which case create the new line,
4739 // find the child corresponding to the start position of the string, and
4740 // continue.
4741
4742 wxRect availableRect;
4743
4744 node = m_children.GetFirst();
4745 while (node)
4746 {
4747 wxRichTextObject* child = node->GetData();
4748
4749 // If floating, ignore. We already laid out floats.
4750 // Also ignore if empty object, except if we haven't got any
4751 // size yet.
4752 if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4753 || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4754 )
4755 {
4756 node = node->GetNext();
4757 continue;
4758 }
4759
4760 // If this is e.g. a composite text box, it will need to be laid out itself.
4761 // But if just a text fragment or image, for example, this will
4762 // do nothing. NB: won't we need to set the position after layout?
4763 // since for example if position is dependent on vertical line size, we
4764 // can't tell the position until the size is determined. So possibly introduce
4765 // another layout phase.
4766
4767 // We may only be looking at part of a child, if we searched back for wrapping
4768 // and found a suitable point some way into the child. So get the size for the fragment
4769 // if necessary.
4770
4771 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4772 long lastPosToUse = child->GetRange().GetEnd();
4773 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
4774
4775 if (lineBreakInThisObject)
4776 lastPosToUse = nextBreakPos;
4777
4778 wxSize childSize;
4779 int childDescent = 0;
4780
4781 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4782 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4783 rect.width - startOffset - rightIndent, rect.height);
4784
4785 if (child->IsTopLevel())
4786 {
4787 wxSize oldSize = child->GetCachedSize();
4788
4789 child->Invalidate(wxRICHTEXT_ALL);
4790 child->SetPosition(wxPoint(0, 0));
4791
4792 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4793 // lays out the object again using the minimum size
4794 // The position will be determined by its location in its line,
4795 // and not by the child's actual position.
4796 child->LayoutToBestSize(dc, context, buffer,
4797 attr, child->GetAttributes(), availableRect, parentRect, style);
4798
4799 if (oldSize != child->GetCachedSize())
4800 {
4801 partialExtents.Clear();
4802
4803 // Recalculate the partial text extents since the child object changed size
4804 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4805 }
4806 }
4807
4808 // Problem: we need to layout composites here for which we need the available width,
4809 // but we can't get the available width without using the float collector which
4810 // needs to know the object height.
4811
4812 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4813 {
4814 childSize = child->GetCachedSize();
4815 childDescent = child->GetDescent();
4816 }
4817 else
4818 {
4819 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4820 // Get height only, then the width using the partial extents
4821 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4822 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4823 #else
4824 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
4825 #endif
4826 }
4827
4828 bool doLoop = true;
4829 int loopIterations = 0;
4830
4831 // If there are nested objects that need to lay themselves out, we have to do this in a
4832 // loop because the height of the object may well depend on the available width.
4833 // And because of floating object positioning, the available width depends on the
4834 // height of the object and whether it will clash with the floating objects.
4835 // So, we see whether the available width changes due to the presence of floating images.
4836 // If it does, then we'll use the new restricted width to find the object height again.
4837 // If this causes another restriction in the available width, we'll try again, until
4838 // either we lose patience or the available width settles down.
4839 do
4840 {
4841 loopIterations ++;
4842
4843 wxRect oldAvailableRect = availableRect;
4844
4845 // Available width depends on the floating objects and the line height.
4846 // Note: the floating objects may be placed vertically along the two sides of
4847 // buffer, so we may have different available line widths with different
4848 // [startY, endY]. So, we can't determine how wide the available
4849 // space is until we know the exact line height.
4850 if (childDescent == 0)
4851 {
4852 lineHeight = wxMax(lineHeight, childSize.y);
4853 lineDescent = maxDescent;
4854 lineAscent = maxAscent;
4855 }
4856 else
4857 {
4858 lineDescent = wxMax(childDescent, maxDescent);
4859 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4860 }
4861 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
4862
4863 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
4864 {
4865 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4866
4867 // Adjust availableRect to the space that is available when taking floating objects into account.
4868
4869 if (floatAvailableRect.x + startOffset > availableRect.x)
4870 {
4871 int newX = floatAvailableRect.x + startOffset;
4872 int newW = availableRect.width - (newX - availableRect.x);
4873 availableRect.x = newX;
4874 availableRect.width = newW;
4875 }
4876
4877 if (floatAvailableRect.width < availableRect.width)
4878 availableRect.width = floatAvailableRect.width;
4879 }
4880
4881 currentPosition.x = availableRect.x - rect.x;
4882
4883 if (child->IsTopLevel() && loopIterations <= 20)
4884 {
4885 if (availableRect != oldAvailableRect)
4886 {
4887 wxSize oldSize = child->GetCachedSize();
4888
4889 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4890 // lays out the object again using the minimum size
4891 child->Invalidate(wxRICHTEXT_ALL);
4892 child->LayoutToBestSize(dc, context, buffer,
4893 attr, child->GetAttributes(), availableRect, parentRect, style);
4894 childSize = child->GetCachedSize();
4895 childDescent = child->GetDescent();
4896
4897 if (oldSize != child->GetCachedSize())
4898 {
4899 partialExtents.Clear();
4900
4901 // Recalculate the partial text extents since the child object changed size
4902 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4903 }
4904
4905 // Go around the loop finding the available rect for the given floating objects
4906 }
4907 else
4908 doLoop = false;
4909 }
4910 else
4911 doLoop = false;
4912 }
4913 while (doLoop);
4914
4915 if (child->IsTopLevel())
4916 {
4917 // We can move it to the correct position at this point
4918 // TODO: probably need to add margin
4919 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
4920 }
4921
4922 // Cases:
4923 // 1) There was a line break BEFORE the natural break
4924 // 2) There was a line break AFTER the natural break
4925 // 3) It's the last line
4926 // 4) The child still fits (carry on) - 'else' clause
4927
4928 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4929 ||
4930 (childSize.x + currentWidth > availableRect.width)
4931 ||
4932 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4933
4934 )
4935 {
4936 long wrapPosition = 0;
4937 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4938 wrapPosition = child->GetRange().GetEnd();
4939 else
4940
4941 // Find a place to wrap. This may walk back to previous children,
4942 // for example if a word spans several objects.
4943 // Note: one object must contains only one wxTextAtrr, so the line height will not
4944 // change inside one object. Thus, we can pass the remain line width to the
4945 // FindWrapPosition function.
4946 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
4947 {
4948 // If the function failed, just cut it off at the end of this child.
4949 wrapPosition = child->GetRange().GetEnd();
4950 }
4951
4952 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4953 if (wrapPosition <= lastCompletedEndPos)
4954 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4955
4956 // Line end position shouldn't be the same as the end, or greater.
4957 if (wrapPosition >= GetRange().GetEnd())
4958 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
4959
4960 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4961
4962 // Let's find the actual size of the current line now
4963 wxSize actualSize;
4964 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4965
4966 childDescent = 0;
4967
4968 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4969 if (!child->IsEmpty())
4970 {
4971 // Get height only, then the width using the partial extents
4972 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4973 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4974 }
4975 else
4976 #endif
4977 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
4978
4979 currentWidth = actualSize.x;
4980
4981 // The descent for the whole line at this point, is the correct max descent
4982 maxDescent = childDescent;
4983 // Maximum ascent
4984 maxAscent = actualSize.y-childDescent;
4985
4986 // lineHeight is given by the height for the whole line, since it will
4987 // take into account ascend/descend.
4988 lineHeight = actualSize.y;
4989
4990 if (lineHeight == 0 && buffer)
4991 {
4992 wxFont font(buffer->GetFontTable().FindFont(attr));
4993 wxCheckSetFont(dc, font);
4994 lineHeight = dc.GetCharHeight();
4995 }
4996
4997 if (maxDescent == 0)
4998 {
4999 int w, h;
5000 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5001 }
5002
5003 // Add a new line
5004 wxRichTextLine* line = AllocateLine(lineCount);
5005
5006 // Set relative range so we won't have to change line ranges when paragraphs are moved
5007 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5008 line->SetPosition(currentPosition);
5009 line->SetSize(wxSize(currentWidth, lineHeight));
5010 line->SetDescent(maxDescent);
5011
5012 maxHeight = currentPosition.y + lineHeight;
5013
5014 // Now move down a line. TODO: add margins, spacing
5015 currentPosition.y += lineHeight;
5016 currentPosition.y += lineSpacing;
5017 maxDescent = 0;
5018 maxAscent = 0;
5019 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5020 currentWidth = 0;
5021
5022 lineCount ++;
5023
5024 // TODO: account for zero-length objects
5025 // wxASSERT(wrapPosition > lastCompletedEndPos);
5026
5027 lastEndPos = wrapPosition;
5028 lastCompletedEndPos = lastEndPos;
5029
5030 lineHeight = 0;
5031
5032 if (wrapPosition < GetRange().GetEnd()-1)
5033 {
5034 // May need to set the node back to a previous one, due to searching back in wrapping
5035 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5036 if (childAfterWrapPosition)
5037 node = m_children.Find(childAfterWrapPosition);
5038 else
5039 node = node->GetNext();
5040 }
5041 else
5042 node = node->GetNext();
5043
5044 // Apply paragraph styles such as alignment to the wrapped line
5045 ApplyParagraphStyle(line, attr, availableRect, dc);
5046 }
5047 else
5048 {
5049 // We still fit, so don't add a line, and keep going
5050 currentWidth += childSize.x;
5051
5052 if (childDescent == 0)
5053 {
5054 // An object with a zero descend value wants to take up the whole
5055 // height regardless of baseline
5056 lineHeight = wxMax(lineHeight, childSize.y);
5057 }
5058 else
5059 {
5060 maxDescent = wxMax(childDescent, maxDescent);
5061 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5062 }
5063
5064 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5065
5066 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5067 lastEndPos = child->GetRange().GetEnd();
5068
5069 node = node->GetNext();
5070 }
5071 }
5072
5073 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5074
5075 // Remove remaining unused line objects, if any
5076 ClearUnusedLines(lineCount);
5077
5078 // We need to add back the margins etc.
5079 {
5080 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5081 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
5082 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5083 SetCachedSize(marginRect.GetSize());
5084 }
5085
5086 // The maximum size is the length of the paragraph stretched out into a line.
5087 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5088 // this size. TODO: take into account line breaks.
5089 {
5090 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5091 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
5092 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5093 SetMaxSize(marginRect.GetSize());
5094 }
5095
5096 // Find the greatest minimum size. Currently we only look at non-text objects,
5097 // which isn't ideal but it would be slow to find the maximum word width to
5098 // use as the minimum.
5099 {
5100 int minWidth = 0;
5101 node = m_children.GetFirst();
5102 while (node)
5103 {
5104 wxRichTextObject* child = node->GetData();
5105
5106 // If floating, ignore. We already laid out floats.
5107 // Also ignore if empty object, except if we haven't got any
5108 // size yet.
5109 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
5110 {
5111 if (child->GetCachedSize().x > minWidth)
5112 minWidth = child->GetMinSize().x;
5113 }
5114 node = node->GetNext();
5115 }
5116
5117 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5118 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
5119 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5120 SetMinSize(marginRect.GetSize());
5121 }
5122
5123 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5124 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5125 // Use the text extents to calculate the size of each fragment in each line
5126 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5127 while (lineNode)
5128 {
5129 wxRichTextLine* line = lineNode->GetData();
5130 wxRichTextRange lineRange = line->GetAbsoluteRange();
5131
5132 // Loop through objects until we get to the one within range
5133 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5134
5135 while (node2)
5136 {
5137 wxRichTextObject* child = node2->GetData();
5138
5139 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
5140 {
5141 wxRichTextRange rangeToUse = lineRange;
5142 rangeToUse.LimitTo(child->GetRange());
5143
5144 // Find the size of the child from the text extents, and store in an array
5145 // for drawing later
5146 int left = 0;
5147 if (rangeToUse.GetStart() > GetRange().GetStart())
5148 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5149 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5150 int sz = right - left;
5151 line->GetObjectSizes().Add(sz);
5152 }
5153 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5154 // Can break out of inner loop now since we've passed this line's range
5155 break;
5156
5157 node2 = node2->GetNext();
5158 }
5159
5160 lineNode = lineNode->GetNext();
5161 }
5162 #endif
5163 #endif
5164
5165 return true;
5166 }
5167
5168 /// Apply paragraph styles, such as centering, to wrapped lines
5169 /// TODO: take into account box attributes, possibly
5170 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5171 {
5172 if (!attr.HasAlignment())
5173 return;
5174
5175 wxPoint pos = line->GetPosition();
5176 wxPoint originalPos = pos;
5177 wxSize size = line->GetSize();
5178
5179 // centering, right-justification
5180 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
5181 {
5182 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5183 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5184 line->SetPosition(pos);
5185 }
5186 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
5187 {
5188 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5189 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5190 line->SetPosition(pos);
5191 }
5192
5193 if (pos != originalPos)
5194 {
5195 wxPoint inc = pos - originalPos;
5196
5197 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5198
5199 while (node)
5200 {
5201 wxRichTextObject* child = node->GetData();
5202 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5203 child->Move(child->GetPosition() + inc);
5204
5205 node = node->GetNext();
5206 }
5207 }
5208 }
5209
5210 /// Insert text at the given position
5211 bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5212 {
5213 wxRichTextObject* childToUse = NULL;
5214 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5215
5216 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5217 while (node)
5218 {
5219 wxRichTextObject* child = node->GetData();
5220 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5221 {
5222 childToUse = child;
5223 nodeToUse = node;
5224 break;
5225 }
5226
5227 node = node->GetNext();
5228 }
5229
5230 if (childToUse)
5231 {
5232 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5233 if (textObject)
5234 {
5235 int posInString = pos - textObject->GetRange().GetStart();
5236
5237 wxString newText = textObject->GetText().Mid(0, posInString) +
5238 text + textObject->GetText().Mid(posInString);
5239 textObject->SetText(newText);
5240
5241 int textLength = text.length();
5242
5243 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5244 textObject->GetRange().GetEnd() + textLength));
5245
5246 // Increment the end range of subsequent fragments in this paragraph.
5247 // We'll set the paragraph range itself at a higher level.
5248
5249 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5250 while (node)
5251 {
5252 wxRichTextObject* child = node->GetData();
5253 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5254 textObject->GetRange().GetEnd() + textLength));
5255
5256 node = node->GetNext();
5257 }
5258
5259 return true;
5260 }
5261 else
5262 {
5263 // TODO: if not a text object, insert at closest position, e.g. in front of it
5264 }
5265 }
5266 else
5267 {
5268 // Add at end.
5269 // Don't pass parent initially to suppress auto-setting of parent range.
5270 // We'll do that at a higher level.
5271 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5272
5273 AppendChild(textObject);
5274 return true;
5275 }
5276
5277 return false;
5278 }
5279
5280 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5281 {
5282 wxRichTextCompositeObject::Copy(obj);
5283 }
5284
5285 /// Clear the cached lines
5286 void wxRichTextParagraph::ClearLines()
5287 {
5288 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5289 }
5290
5291 /// Get/set the object size for the given range. Returns false if the range
5292 /// is invalid for this object.
5293 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
5294 {
5295 if (!range.IsWithin(GetRange()))
5296 return false;
5297
5298 if (flags & wxRICHTEXT_UNFORMATTED)
5299 {
5300 // Just use unformatted data, assume no line breaks
5301 wxSize sz;
5302
5303 wxArrayInt childExtents;
5304 wxArrayInt* p;
5305 if (partialExtents)
5306 p = & childExtents;
5307 else
5308 p = NULL;
5309
5310 int maxDescent = 0;
5311 int maxAscent = 0;
5312 int maxLineHeight = 0;
5313
5314 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5315 while (node)
5316 {
5317 wxRichTextObject* child = node->GetData();
5318 if (!child->GetRange().IsOutside(range))
5319 {
5320 // Floating objects have a zero size within the paragraph.
5321 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5322 {
5323 if (partialExtents)
5324 {
5325 int lastSize;
5326 if (partialExtents->GetCount() > 0)
5327 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5328 else
5329 lastSize = 0;
5330
5331 partialExtents->Add(0 /* zero size */ + lastSize);
5332 }
5333 }
5334 else
5335 {
5336 wxSize childSize;
5337
5338 wxRichTextRange rangeToUse = range;
5339 rangeToUse.LimitTo(child->GetRange());
5340 int childDescent = 0;
5341
5342 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5343 // but it's only going to be used after caching has taken place.
5344 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
5345 {
5346 childDescent = child->GetDescent();
5347 childSize = child->GetCachedSize();
5348
5349 if (childDescent == 0)
5350 {
5351 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5352 }
5353 else
5354 {
5355 maxDescent = wxMax(maxDescent, childDescent);
5356 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5357 }
5358
5359 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5360
5361 sz.y = wxMax(sz.y, maxLineHeight);
5362 sz.x += childSize.x;
5363 descent = maxDescent;
5364 }
5365 else if (child->IsTopLevel())
5366 {
5367 childDescent = child->GetDescent();
5368 childSize = child->GetCachedSize();
5369
5370 if (childDescent == 0)
5371 {
5372 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5373 }
5374 else
5375 {
5376 maxDescent = wxMax(maxDescent, childDescent);
5377 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5378 }
5379
5380 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5381
5382 sz.y = wxMax(sz.y, maxLineHeight);
5383 sz.x += childSize.x;
5384 descent = maxDescent;
5385
5386 // FIXME: this won't change the original values.
5387 // Should we be calling GetRangeSize above instead of using cached values?
5388 #if 0
5389 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5390 {
5391 child->SetCachedSize(childSize);
5392 child->SetDescent(childDescent);
5393 }
5394 #endif
5395
5396 if (partialExtents)
5397 {
5398 int lastSize;
5399 if (partialExtents->GetCount() > 0)
5400 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5401 else
5402 lastSize = 0;
5403
5404 partialExtents->Add(childSize.x + lastSize);
5405 }
5406 }
5407 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
5408 {
5409 if (childDescent == 0)
5410 {
5411 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5412 }
5413 else
5414 {
5415 maxDescent = wxMax(maxDescent, childDescent);
5416 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5417 }
5418
5419 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5420
5421 sz.y = wxMax(sz.y, maxLineHeight);
5422 sz.x += childSize.x;
5423 descent = maxDescent;
5424
5425 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5426 {
5427 child->SetCachedSize(childSize);
5428 child->SetDescent(childDescent);
5429 }
5430
5431 if (partialExtents)
5432 {
5433 int lastSize;
5434 if (partialExtents->GetCount() > 0)
5435 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5436 else
5437 lastSize = 0;
5438
5439 size_t i;
5440 for (i = 0; i < childExtents.GetCount(); i++)
5441 {
5442 partialExtents->Add(childExtents[i] + lastSize);
5443 }
5444 }
5445 }
5446 }
5447
5448 if (p)
5449 p->Clear();
5450 }
5451
5452 node = node->GetNext();
5453 }
5454 size = sz;
5455 }
5456 else
5457 {
5458 // Use formatted data, with line breaks
5459 wxSize sz;
5460
5461 // We're going to loop through each line, and then for each line,
5462 // call GetRangeSize for the fragment that comprises that line.
5463 // Only we have to do that multiple times within the line, because
5464 // the line may be broken into pieces. For now ignore line break commands
5465 // (so we can assume that getting the unformatted size for a fragment
5466 // within a line is the actual size)
5467
5468 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5469 while (node)
5470 {
5471 wxRichTextLine* line = node->GetData();
5472 wxRichTextRange lineRange = line->GetAbsoluteRange();
5473 if (!lineRange.IsOutside(range))
5474 {
5475 int maxDescent = 0;
5476 int maxAscent = 0;
5477 int maxLineHeight = 0;
5478 int maxLineWidth = 0;
5479
5480 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5481 while (node2)
5482 {
5483 wxRichTextObject* child = node2->GetData();
5484
5485 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5486 {
5487 wxRichTextRange rangeToUse = lineRange;
5488 rangeToUse.LimitTo(child->GetRange());
5489 if (child->IsTopLevel())
5490 rangeToUse = child->GetOwnRange();
5491
5492 wxSize childSize;
5493 int childDescent = 0;
5494 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
5495 {
5496 if (childDescent == 0)
5497 {
5498 // Assume that if descent is zero, this child can occupy the full line height
5499 // and does not need space for the line's maximum descent. So we influence
5500 // the overall max line height only.
5501 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5502 }
5503 else
5504 {
5505 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5506 maxDescent = wxMax(maxAscent, childDescent);
5507 }
5508 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5509 maxLineWidth += childSize.x;
5510 }
5511 }
5512
5513 node2 = node2->GetNext();
5514 }
5515
5516 descent = wxMax(descent, maxDescent);
5517
5518 // Increase size by a line (TODO: paragraph spacing)
5519 sz.y += maxLineHeight;
5520 sz.x = wxMax(sz.x, maxLineWidth);
5521 }
5522 node = node->GetNext();
5523 }
5524 size = sz;
5525 }
5526 return true;
5527 }
5528
5529 /// Finds the absolute position and row height for the given character position
5530 bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5531 {
5532 if (index == -1)
5533 {
5534 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5535 if (line)
5536 *height = line->GetSize().y;
5537 else
5538 *height = dc.GetCharHeight();
5539
5540 // -1 means 'the start of the buffer'.
5541 pt = GetPosition();
5542 if (line)
5543 pt = pt + line->GetPosition();
5544
5545 return true;
5546 }
5547
5548 // The final position in a paragraph is taken to mean the position
5549 // at the start of the next paragraph.
5550 if (index == GetRange().GetEnd())
5551 {
5552 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5553 wxASSERT( parent != NULL );
5554
5555 // Find the height at the next paragraph, if any
5556 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5557 if (line)
5558 {
5559 *height = line->GetSize().y;
5560 pt = line->GetAbsolutePosition();
5561 }
5562 else
5563 {
5564 *height = dc.GetCharHeight();
5565 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5566 pt = wxPoint(indent, GetCachedSize().y);
5567 }
5568
5569 return true;
5570 }
5571
5572 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5573 return false;
5574
5575 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5576 while (node)
5577 {
5578 wxRichTextLine* line = node->GetData();
5579 wxRichTextRange lineRange = line->GetAbsoluteRange();
5580 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5581 {
5582 // If this is the last point in the line, and we're forcing the
5583 // returned value to be the start of the next line, do the required
5584 // thing.
5585 if (index == lineRange.GetEnd() && forceLineStart)
5586 {
5587 if (node->GetNext())
5588 {
5589 wxRichTextLine* nextLine = node->GetNext()->GetData();
5590 *height = nextLine->GetSize().y;
5591 pt = nextLine->GetAbsolutePosition();
5592 return true;
5593 }
5594 }
5595
5596 pt.y = line->GetPosition().y + GetPosition().y;
5597
5598 wxRichTextRange r(lineRange.GetStart(), index);
5599 wxSize rangeSize;
5600 int descent = 0;
5601
5602 // We find the size of the line up to this point,
5603 // then we can add this size to the line start position and
5604 // paragraph start position to find the actual position.
5605
5606 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5607 {
5608 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5609 *height = line->GetSize().y;
5610
5611 return true;
5612 }
5613
5614 }
5615
5616 node = node->GetNext();
5617 }
5618
5619 return false;
5620 }
5621
5622 /// Hit-testing: returns a flag indicating hit test details, plus
5623 /// information about position
5624 int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5625 {
5626 if (!IsShown())
5627 return wxRICHTEXT_HITTEST_NONE;
5628
5629 // If we're in the top-level container, then we can return
5630 // a suitable hit test code even if the point is outside the container area,
5631 // so that we can position the caret sensibly even if we don't
5632 // click on valid content. If we're not at the top-level, and the point
5633 // is not within this paragraph object, then we don't want to stop more
5634 // precise hit-testing from working prematurely, so return immediately.
5635 // NEW STRATEGY: use the parent boundary to test whether we're in the
5636 // right region, not the paragraph, since the paragraph may be positioned
5637 // some way in from where the user clicks.
5638 {
5639 long tmpPos;
5640 wxRichTextObject* tempObj, *tempContextObj;
5641 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
5642 return wxRICHTEXT_HITTEST_NONE;
5643 }
5644
5645 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5646 while (objNode)
5647 {
5648 wxRichTextObject* child = objNode->GetData();
5649 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5650 // and also, if this seems composite but actually is marked as atomic,
5651 // don't recurse.
5652 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5653 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
5654 {
5655 {
5656 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
5657 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5658 return hitTest;
5659 }
5660 }
5661
5662 objNode = objNode->GetNext();
5663 }
5664
5665 wxPoint paraPos = GetPosition();
5666
5667 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5668 while (node)
5669 {
5670 wxRichTextLine* line = node->GetData();
5671 wxPoint linePos = paraPos + line->GetPosition();
5672 wxSize lineSize = line->GetSize();
5673 wxRichTextRange lineRange = line->GetAbsoluteRange();
5674
5675 if (pt.y <= linePos.y + lineSize.y)
5676 {
5677 if (pt.x < linePos.x)
5678 {
5679 textPosition = lineRange.GetStart();
5680 *obj = FindObjectAtPosition(textPosition);
5681 *contextObj = GetContainer();
5682 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5683 }
5684 else if (pt.x >= (linePos.x + lineSize.x))
5685 {
5686 textPosition = lineRange.GetEnd();
5687 *obj = FindObjectAtPosition(textPosition);
5688 *contextObj = GetContainer();
5689 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5690 }
5691 else
5692 {
5693 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5694 wxArrayInt partialExtents;
5695
5696 wxSize paraSize;
5697 int paraDescent;
5698
5699 // This calculates the partial text extents
5700 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
5701
5702 int lastX = linePos.x;
5703 size_t i;
5704 for (i = 0; i < partialExtents.GetCount(); i++)
5705 {
5706 int nextX = partialExtents[i] + linePos.x;
5707
5708 if (pt.x >= lastX && pt.x <= nextX)
5709 {
5710 textPosition = i + lineRange.GetStart(); // minus 1?
5711
5712 *obj = FindObjectAtPosition(textPosition);
5713 *contextObj = GetContainer();
5714
5715 // So now we know it's between i-1 and i.
5716 // Let's see if we can be more precise about
5717 // which side of the position it's on.
5718
5719 int midPoint = (nextX + lastX)/2;
5720 if (pt.x >= midPoint)
5721 return wxRICHTEXT_HITTEST_AFTER;
5722 else
5723 return wxRICHTEXT_HITTEST_BEFORE;
5724 }
5725
5726 lastX = nextX;
5727 }
5728 #else
5729 long i;
5730 int lastX = linePos.x;
5731 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5732 {
5733 wxSize childSize;
5734 int descent = 0;
5735
5736 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
5737
5738 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5739
5740 int nextX = childSize.x + linePos.x;
5741
5742 if (pt.x >= lastX && pt.x <= nextX)
5743 {
5744 textPosition = i;
5745
5746 *obj = FindObjectAtPosition(textPosition);
5747 *contextObj = GetContainer();
5748
5749 // So now we know it's between i-1 and i.
5750 // Let's see if we can be more precise about
5751 // which side of the position it's on.
5752
5753 int midPoint = (nextX + lastX)/2;
5754 if (pt.x >= midPoint)
5755 return wxRICHTEXT_HITTEST_AFTER;
5756 else
5757 return wxRICHTEXT_HITTEST_BEFORE;
5758 }
5759 else
5760 {
5761 lastX = nextX;
5762 }
5763 }
5764 #endif
5765 }
5766 }
5767
5768 node = node->GetNext();
5769 }
5770
5771 return wxRICHTEXT_HITTEST_NONE;
5772 }
5773
5774 /// Split an object at this position if necessary, and return
5775 /// the previous object, or NULL if inserting at beginning.
5776 wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5777 {
5778 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5779 while (node)
5780 {
5781 wxRichTextObject* child = node->GetData();
5782
5783 if (pos == child->GetRange().GetStart())
5784 {
5785 if (previousObject)
5786 {
5787 if (node->GetPrevious())
5788 *previousObject = node->GetPrevious()->GetData();
5789 else
5790 *previousObject = NULL;
5791 }
5792
5793 return child;
5794 }
5795
5796 if (child->GetRange().Contains(pos))
5797 {
5798 // This should create a new object, transferring part of
5799 // the content to the old object and the rest to the new object.
5800 wxRichTextObject* newObject = child->DoSplit(pos);
5801
5802 // If we couldn't split this object, just insert in front of it.
5803 if (!newObject)
5804 {
5805 // Maybe this is an empty string, try the next one
5806 // return child;
5807 }
5808 else
5809 {
5810 // Insert the new object after 'child'
5811 if (node->GetNext())
5812 m_children.Insert(node->GetNext(), newObject);
5813 else
5814 m_children.Append(newObject);
5815 newObject->SetParent(this);
5816
5817 if (previousObject)
5818 *previousObject = child;
5819
5820 return newObject;
5821 }
5822 }
5823
5824 node = node->GetNext();
5825 }
5826 if (previousObject)
5827 *previousObject = NULL;
5828 return NULL;
5829 }
5830
5831 /// Move content to a list from obj on
5832 void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5833 {
5834 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5835 while (node)
5836 {
5837 wxRichTextObject* child = node->GetData();
5838 list.Append(child);
5839
5840 wxRichTextObjectList::compatibility_iterator oldNode = node;
5841
5842 node = node->GetNext();
5843
5844 m_children.DeleteNode(oldNode);
5845 }
5846 }
5847
5848 /// Add content back from list
5849 void wxRichTextParagraph::MoveFromList(wxList& list)
5850 {
5851 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5852 {
5853 AppendChild((wxRichTextObject*) node->GetData());
5854 }
5855 }
5856
5857 /// Calculate range
5858 void wxRichTextParagraph::CalculateRange(long start, long& end)
5859 {
5860 wxRichTextCompositeObject::CalculateRange(start, end);
5861
5862 // Add one for end of paragraph
5863 end ++;
5864
5865 m_range.SetRange(start, end);
5866 }
5867
5868 /// Find the object at the given position
5869 wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5870 {
5871 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5872 while (node)
5873 {
5874 wxRichTextObject* obj = node->GetData();
5875 if (obj->GetRange().Contains(position) ||
5876 obj->GetRange().GetStart() == position ||
5877 obj->GetRange().GetEnd() == position)
5878 return obj;
5879
5880 node = node->GetNext();
5881 }
5882 return NULL;
5883 }
5884
5885 /// Get the plain text searching from the start or end of the range.
5886 /// The resulting string may be shorter than the range given.
5887 bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5888 {
5889 text = wxEmptyString;
5890
5891 if (fromStart)
5892 {
5893 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5894 while (node)
5895 {
5896 wxRichTextObject* obj = node->GetData();
5897 if (!obj->GetRange().IsOutside(range))
5898 {
5899 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5900 if (textObj)
5901 {
5902 text += textObj->GetTextForRange(range);
5903 }
5904 else
5905 {
5906 text += wxT(" ");
5907 }
5908 }
5909
5910 node = node->GetNext();
5911 }
5912 }
5913 else
5914 {
5915 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5916 while (node)
5917 {
5918 wxRichTextObject* obj = node->GetData();
5919 if (!obj->GetRange().IsOutside(range))
5920 {
5921 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5922 if (textObj)
5923 {
5924 text = textObj->GetTextForRange(range) + text;
5925 }
5926 else
5927 {
5928 text = wxT(" ") + text;
5929 }
5930 }
5931
5932 node = node->GetPrevious();
5933 }
5934 }
5935
5936 return true;
5937 }
5938
5939 /// Find a suitable wrap position.
5940 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5941 {
5942 if (range.GetLength() <= 0)
5943 return false;
5944
5945 // Find the first position where the line exceeds the available space.
5946 wxSize sz;
5947 long breakPosition = range.GetEnd();
5948
5949 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5950 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5951 {
5952 int widthBefore;
5953
5954 if (range.GetStart() > GetRange().GetStart())
5955 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5956 else
5957 widthBefore = 0;
5958
5959 size_t i;
5960 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5961 {
5962 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
5963
5964 if (widthFromStartOfThisRange > availableSpace)
5965 {
5966 breakPosition = i-1;
5967 break;
5968 }
5969 }
5970 }
5971 else
5972 #endif
5973 {
5974 // Binary chop for speed
5975 long minPos = range.GetStart();
5976 long maxPos = range.GetEnd();
5977 while (true)
5978 {
5979 if (minPos == maxPos)
5980 {
5981 int descent = 0;
5982 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
5983
5984 if (sz.x > availableSpace)
5985 breakPosition = minPos - 1;
5986 break;
5987 }
5988 else if ((maxPos - minPos) == 1)
5989 {
5990 int descent = 0;
5991 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
5992
5993 if (sz.x > availableSpace)
5994 breakPosition = minPos - 1;
5995 else
5996 {
5997 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
5998 if (sz.x > availableSpace)
5999 breakPosition = maxPos-1;
6000 }
6001 break;
6002 }
6003 else
6004 {
6005 long nextPos = minPos + ((maxPos - minPos) / 2);
6006
6007 int descent = 0;
6008 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6009
6010 if (sz.x > availableSpace)
6011 {
6012 maxPos = nextPos;
6013 }
6014 else
6015 {
6016 minPos = nextPos;
6017 }
6018 }
6019 }
6020 }
6021
6022 // Now we know the last position on the line.
6023 // Let's try to find a word break.
6024
6025 wxString plainText;
6026 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6027 {
6028 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6029 if (newLinePos != wxNOT_FOUND)
6030 {
6031 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6032 }
6033 else
6034 {
6035 int spacePos = plainText.Find(wxT(' '), true);
6036 int tabPos = plainText.Find(wxT('\t'), true);
6037 int pos = wxMax(spacePos, tabPos);
6038 if (pos != wxNOT_FOUND)
6039 {
6040 int positionsFromEndOfString = plainText.length() - pos - 1;
6041 breakPosition = breakPosition - positionsFromEndOfString;
6042 }
6043 }
6044 }
6045
6046 wrapPosition = breakPosition;
6047
6048 return true;
6049 }
6050
6051 /// Get the bullet text for this paragraph.
6052 wxString wxRichTextParagraph::GetBulletText()
6053 {
6054 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6055 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6056 return wxEmptyString;
6057
6058 int number = GetAttributes().GetBulletNumber();
6059
6060 wxString text;
6061 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
6062 {
6063 text.Printf(wxT("%d"), number);
6064 }
6065 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6066 {
6067 // TODO: Unicode, and also check if number > 26
6068 text.Printf(wxT("%c"), (wxChar) (number+64));
6069 }
6070 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6071 {
6072 // TODO: Unicode, and also check if number > 26
6073 text.Printf(wxT("%c"), (wxChar) (number+96));
6074 }
6075 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6076 {
6077 text = wxRichTextDecimalToRoman(number);
6078 }
6079 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6080 {
6081 text = wxRichTextDecimalToRoman(number);
6082 text.MakeLower();
6083 }
6084 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6085 {
6086 text = GetAttributes().GetBulletText();
6087 }
6088
6089 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6090 {
6091 // The outline style relies on the text being computed statically,
6092 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6093 // should be stored in the attributes; if not, just use the number for this
6094 // level, as previously computed.
6095 if (!GetAttributes().GetBulletText().IsEmpty())
6096 text = GetAttributes().GetBulletText();
6097 }
6098
6099 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6100 {
6101 text = wxT("(") + text + wxT(")");
6102 }
6103 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6104 {
6105 text = text + wxT(")");
6106 }
6107
6108 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6109 {
6110 text += wxT(".");
6111 }
6112
6113 return text;
6114 }
6115
6116 /// Allocate or reuse a line object
6117 wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6118 {
6119 if (pos < (int) m_cachedLines.GetCount())
6120 {
6121 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6122 line->Init(this);
6123 return line;
6124 }
6125 else
6126 {
6127 wxRichTextLine* line = new wxRichTextLine(this);
6128 m_cachedLines.Append(line);
6129 return line;
6130 }
6131 }
6132
6133 /// Clear remaining unused line objects, if any
6134 bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6135 {
6136 int cachedLineCount = m_cachedLines.GetCount();
6137 if ((int) cachedLineCount > lineCount)
6138 {
6139 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6140 {
6141 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6142 wxRichTextLine* line = node->GetData();
6143 m_cachedLines.Erase(node);
6144 delete line;
6145 }
6146 }
6147 return true;
6148 }
6149
6150 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6151 /// retrieve the actual style.
6152 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
6153 {
6154 wxRichTextAttr attr;
6155 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6156 if (buf)
6157 {
6158 attr = buf->GetBasicStyle();
6159 if (!includingBoxAttr)
6160 {
6161 attr.GetTextBoxAttr().Reset();
6162 // The background colour will be painted by the container, and we don't
6163 // want to unnecessarily overwrite the background when we're drawing text
6164 // because this may erase the guideline (which appears just under the text
6165 // if there's no padding).
6166 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6167 }
6168 wxRichTextApplyStyle(attr, GetAttributes());
6169 }
6170 else
6171 attr = GetAttributes();
6172
6173 wxRichTextApplyStyle(attr, contentStyle);
6174 return attr;
6175 }
6176
6177 /// Get combined attributes of the base style and paragraph style.
6178 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
6179 {
6180 wxRichTextAttr attr;
6181 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6182 if (buf)
6183 {
6184 attr = buf->GetBasicStyle();
6185 if (!includingBoxAttr)
6186 attr.GetTextBoxAttr().Reset();
6187 wxRichTextApplyStyle(attr, GetAttributes());
6188 }
6189 else
6190 attr = GetAttributes();
6191
6192 return attr;
6193 }
6194
6195 // Create default tabstop array
6196 void wxRichTextParagraph::InitDefaultTabs()
6197 {
6198 // create a default tab list at 10 mm each.
6199 for (int i = 0; i < 20; ++i)
6200 {
6201 sm_defaultTabs.Add(i*100);
6202 }
6203 }
6204
6205 // Clear default tabstop array
6206 void wxRichTextParagraph::ClearDefaultTabs()
6207 {
6208 sm_defaultTabs.Clear();
6209 }
6210
6211 void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
6212 {
6213 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6214 while (node)
6215 {
6216 wxRichTextObject* anchored = node->GetData();
6217 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
6218 {
6219 int x = 0;
6220 wxRichTextAttr parentAttr(GetAttributes());
6221 context.ApplyVirtualAttributes(parentAttr, this);
6222 #if 1
6223 // 27-09-2012
6224 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6225
6226 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6227 parentAttr, anchored->GetAttributes(),
6228 parentRect, availableSpace,
6229 style);
6230 wxSize size = anchored->GetCachedSize();
6231 #else
6232 wxSize size;
6233 int descent = 0;
6234 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
6235 #endif
6236
6237 int offsetY = 0;
6238 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6239 {
6240 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6241 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6242 {
6243 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6244 }
6245 }
6246
6247 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
6248
6249 /* Update the offset */
6250 int newOffsetY = pos - rect.y;
6251 if (newOffsetY != offsetY)
6252 {
6253 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6254 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6255 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6256 }
6257
6258 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
6259 x = rect.x;
6260 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
6261 x = rect.x + rect.width - size.x;
6262
6263 //anchored->SetPosition(wxPoint(x, pos));
6264 anchored->Move(wxPoint(x, pos)); // should move children
6265 anchored->SetCachedSize(size);
6266 floatCollector->CollectFloat(this, anchored);
6267 }
6268
6269 node = node->GetNext();
6270 }
6271 }
6272
6273 // Get the first position from pos that has a line break character.
6274 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6275 {
6276 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6277 while (node)
6278 {
6279 wxRichTextObject* obj = node->GetData();
6280 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6281 {
6282 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6283 if (textObj)
6284 {
6285 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6286 if (breakPos > -1)
6287 return breakPos;
6288 }
6289 }
6290 node = node->GetNext();
6291 }
6292 return -1;
6293 }
6294
6295 /*!
6296 * wxRichTextLine
6297 * This object represents a line in a paragraph, and stores
6298 * offsets from the start of the paragraph representing the
6299 * start and end positions of the line.
6300 */
6301
6302 wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6303 {
6304 Init(parent);
6305 }
6306
6307 /// Initialisation
6308 void wxRichTextLine::Init(wxRichTextParagraph* parent)
6309 {
6310 m_parent = parent;
6311 m_range.SetRange(-1, -1);
6312 m_pos = wxPoint(0, 0);
6313 m_size = wxSize(0, 0);
6314 m_descent = 0;
6315 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6316 m_objectSizes.Clear();
6317 #endif
6318 }
6319
6320 /// Copy
6321 void wxRichTextLine::Copy(const wxRichTextLine& obj)
6322 {
6323 m_range = obj.m_range;
6324 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6325 m_objectSizes = obj.m_objectSizes;
6326 #endif
6327 }
6328
6329 /// Get the absolute object position
6330 wxPoint wxRichTextLine::GetAbsolutePosition() const
6331 {
6332 return m_parent->GetPosition() + m_pos;
6333 }
6334
6335 /// Get the absolute range
6336 wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6337 {
6338 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6339 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6340 return range;
6341 }
6342
6343 /*!
6344 * wxRichTextPlainText
6345 * This object represents a single piece of text.
6346 */
6347
6348 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6349
6350 wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
6351 wxRichTextObject(parent)
6352 {
6353 if (style)
6354 SetAttributes(*style);
6355
6356 m_text = text;
6357 }
6358
6359 #define USE_KERNING_FIX 1
6360
6361 // If insufficient tabs are defined, this is the tab width used
6362 #define WIDTH_FOR_DEFAULT_TABS 50
6363
6364 /// Draw the item
6365 bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
6366 {
6367 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6368 wxASSERT (para != NULL);
6369
6370 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6371 context.ApplyVirtualAttributes(textAttr, this);
6372
6373 // Let's make the assumption for now that for content in a paragraph, including
6374 // text, we never have a discontinuous selection. So we only deal with a
6375 // single range.
6376 wxRichTextRange selectionRange;
6377 if (selection.IsValid())
6378 {
6379 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6380 if (selectionRanges.GetCount() > 0)
6381 selectionRange = selectionRanges[0];
6382 else
6383 selectionRange = wxRICHTEXT_NO_SELECTION;
6384 }
6385 else
6386 selectionRange = wxRICHTEXT_NO_SELECTION;
6387
6388 int offset = GetRange().GetStart();
6389
6390 wxString str = m_text;
6391 if (context.HasVirtualText(this))
6392 {
6393 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6394 str = m_text;
6395 }
6396
6397 // Replace line break characters with spaces
6398 wxString toRemove = wxRichTextLineBreakChar;
6399 str.Replace(toRemove, wxT(" "));
6400 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6401 str.MakeUpper();
6402
6403 long len = range.GetLength();
6404 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
6405
6406 // Test for the optimized situations where all is selected, or none
6407 // is selected.
6408
6409 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6410 wxCheckSetFont(dc, textFont);
6411 int charHeight = dc.GetCharHeight();
6412
6413 int x, y;
6414 if ( textFont.IsOk() )
6415 {
6416 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6417 {
6418 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6419 wxCheckSetFont(dc, textFont);
6420 charHeight = dc.GetCharHeight();
6421 }
6422
6423 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6424 {
6425 if (textFont.IsUsingSizeInPixels())
6426 {
6427 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6428 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6429 x = rect.x;
6430 y = rect.y;
6431 }
6432 else
6433 {
6434 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6435 textFont.SetPointSize(static_cast<int>(size));
6436 x = rect.x;
6437 y = rect.y;
6438 }
6439 wxCheckSetFont(dc, textFont);
6440 }
6441 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6442 {
6443 if (textFont.IsUsingSizeInPixels())
6444 {
6445 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6446 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6447 x = rect.x;
6448 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6449 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6450 }
6451 else
6452 {
6453 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6454 textFont.SetPointSize(static_cast<int>(size));
6455 x = rect.x;
6456 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6457 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6458 }
6459 wxCheckSetFont(dc, textFont);
6460 }
6461 else
6462 {
6463 x = rect.x;
6464 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6465 }
6466 }
6467 else
6468 {
6469 x = rect.x;
6470 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6471 }
6472
6473 // TODO: new selection code
6474
6475 // (a) All selected.
6476 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
6477 {
6478 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
6479 }
6480 // (b) None selected.
6481 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6482 {
6483 // Draw all unselected
6484 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
6485 }
6486 else
6487 {
6488 // (c) Part selected, part not
6489 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6490
6491 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6492
6493 // 1. Initial unselected chunk, if any, up until start of selection.
6494 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6495 {
6496 int r1 = range.GetStart();
6497 int s1 = selectionRange.GetStart()-1;
6498 int fragmentLen = s1 - r1 + 1;
6499 if (fragmentLen < 0)
6500 {
6501 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
6502 }
6503 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
6504
6505 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6506
6507 #if USE_KERNING_FIX
6508 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6509 {
6510 // Compensate for kerning difference
6511 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6512 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
6513
6514 wxCoord w1, h1, w2, h2, w3, h3;
6515 dc.GetTextExtent(stringFragment, & w1, & h1);
6516 dc.GetTextExtent(stringFragment2, & w2, & h2);
6517 dc.GetTextExtent(stringFragment3, & w3, & h3);
6518
6519 int kerningDiff = (w1 + w3) - w2;
6520 x = x - kerningDiff;
6521 }
6522 #endif
6523 }
6524
6525 // 2. Selected chunk, if any.
6526 if (selectionRange.GetEnd() >= range.GetStart())
6527 {
6528 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6529 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6530
6531 int fragmentLen = s2 - s1 + 1;
6532 if (fragmentLen < 0)
6533 {
6534 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
6535 }
6536 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
6537
6538 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
6539
6540 #if USE_KERNING_FIX
6541 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6542 {
6543 // Compensate for kerning difference
6544 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6545 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
6546
6547 wxCoord w1, h1, w2, h2, w3, h3;
6548 dc.GetTextExtent(stringFragment, & w1, & h1);
6549 dc.GetTextExtent(stringFragment2, & w2, & h2);
6550 dc.GetTextExtent(stringFragment3, & w3, & h3);
6551
6552 int kerningDiff = (w1 + w3) - w2;
6553 x = x - kerningDiff;
6554 }
6555 #endif
6556 }
6557
6558 // 3. Remaining unselected chunk, if any
6559 if (selectionRange.GetEnd() < range.GetEnd())
6560 {
6561 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6562 int r2 = range.GetEnd();
6563
6564 int fragmentLen = r2 - s2 + 1;
6565 if (fragmentLen < 0)
6566 {
6567 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
6568 }
6569 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
6570
6571 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6572 }
6573 }
6574
6575 return true;
6576 }
6577
6578 bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
6579 {
6580 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6581
6582 wxArrayInt tabArray;
6583 int tabCount;
6584 if (hasTabs)
6585 {
6586 if (attr.GetTabs().IsEmpty())
6587 tabArray = wxRichTextParagraph::GetDefaultTabs();
6588 else
6589 tabArray = attr.GetTabs();
6590 tabCount = tabArray.GetCount();
6591
6592 for (int i = 0; i < tabCount; ++i)
6593 {
6594 int pos = tabArray[i];
6595 pos = ConvertTenthsMMToPixels(dc, pos);
6596 tabArray[i] = pos;
6597 }
6598 }
6599 else
6600 tabCount = 0;
6601
6602 int nextTabPos = -1;
6603 int tabPos = -1;
6604 wxCoord w, h;
6605
6606 if (selected)
6607 {
6608 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6609 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6610
6611 wxCheckSetBrush(dc, wxBrush(highlightColour));
6612 wxCheckSetPen(dc, wxPen(highlightColour));
6613 dc.SetTextForeground(highlightTextColour);
6614 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6615 }
6616 else
6617 {
6618 dc.SetTextForeground(attr.GetTextColour());
6619
6620 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6621 {
6622 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
6623 dc.SetTextBackground(attr.GetBackgroundColour());
6624 }
6625 else
6626 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6627 }
6628
6629 wxCoord x_orig = GetParent()->GetPosition().x;
6630 while (hasTabs)
6631 {
6632 // the string has a tab
6633 // break up the string at the Tab
6634 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6635 str = str.AfterFirst(wxT('\t'));
6636 dc.GetTextExtent(stringChunk, & w, & h);
6637 tabPos = x + w;
6638 bool not_found = true;
6639 for (int i = 0; i < tabCount && not_found; ++i)
6640 {
6641 nextTabPos = tabArray.Item(i) + x_orig;
6642
6643 // Find the next tab position.
6644 // Even if we're at the end of the tab array, we must still draw the chunk.
6645
6646 if (nextTabPos > tabPos || (i == (tabCount - 1)))
6647 {
6648 if (nextTabPos <= tabPos)
6649 {
6650 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6651 nextTabPos = tabPos + defaultTabWidth;
6652 }
6653
6654 not_found = false;
6655 if (selected)
6656 {
6657 w = nextTabPos - x;
6658 wxRect selRect(x, rect.y, w, rect.GetHeight());
6659 dc.DrawRectangle(selRect);
6660 }
6661 dc.DrawText(stringChunk, x, y);
6662
6663 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6664 {
6665 wxPen oldPen = dc.GetPen();
6666 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6667 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6668 wxCheckSetPen(dc, oldPen);
6669 }
6670
6671 x = nextTabPos;
6672 }
6673 }
6674 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6675 }
6676
6677 if (!str.IsEmpty())
6678 {
6679 dc.GetTextExtent(str, & w, & h);
6680 if (selected)
6681 {
6682 wxRect selRect(x, rect.y, w, rect.GetHeight());
6683 dc.DrawRectangle(selRect);
6684 }
6685 dc.DrawText(str, x, y);
6686
6687 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6688 {
6689 wxPen oldPen = dc.GetPen();
6690 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6691 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6692 wxCheckSetPen(dc, oldPen);
6693 }
6694
6695 x += w;
6696 }
6697
6698 return true;
6699 }
6700
6701 /// Lay the item out
6702 bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
6703 {
6704 // Only lay out if we haven't already cached the size
6705 if (m_size.x == -1)
6706 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
6707 m_maxSize = m_size;
6708 // Eventually we want to have a reasonable estimate of minimum size.
6709 m_minSize = wxSize(0, 0);
6710 return true;
6711 }
6712
6713 /// Copy
6714 void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6715 {
6716 wxRichTextObject::Copy(obj);
6717
6718 m_text = obj.m_text;
6719 }
6720
6721 /// Get/set the object size for the given range. Returns false if the range
6722 /// is invalid for this object.
6723 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
6724 {
6725 if (!range.IsWithin(GetRange()))
6726 return false;
6727
6728 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6729 wxASSERT (para != NULL);
6730
6731 int relativeX = position.x - GetParent()->GetPosition().x;
6732
6733 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6734 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
6735
6736 // Always assume unformatted text, since at this level we have no knowledge
6737 // of line breaks - and we don't need it, since we'll calculate size within
6738 // formatted text by doing it in chunks according to the line ranges
6739
6740 bool bScript(false);
6741 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
6742 if (font.IsOk())
6743 {
6744 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6745 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6746 {
6747 wxFont textFont = font;
6748 if (textFont.IsUsingSizeInPixels())
6749 {
6750 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6751 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6752 }
6753 else
6754 {
6755 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6756 textFont.SetPointSize(static_cast<int>(size));
6757 }
6758 wxCheckSetFont(dc, textFont);
6759 bScript = true;
6760 }
6761 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6762 {
6763 wxFont textFont = font;
6764 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6765 wxCheckSetFont(dc, textFont);
6766 bScript = true;
6767 }
6768 else
6769 {
6770 wxCheckSetFont(dc, font);
6771 }
6772 }
6773
6774 bool haveDescent = false;
6775 int startPos = range.GetStart() - GetRange().GetStart();
6776 long len = range.GetLength();
6777
6778 wxString str(m_text);
6779 if (context.HasVirtualText(this))
6780 {
6781 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6782 str = m_text;
6783 }
6784
6785 wxString toReplace = wxRichTextLineBreakChar;
6786 str.Replace(toReplace, wxT(" "));
6787
6788 wxString stringChunk = str.Mid(startPos, (size_t) len);
6789
6790 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6791 stringChunk.MakeUpper();
6792
6793 wxCoord w, h;
6794 int width = 0;
6795 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
6796 {
6797 // the string has a tab
6798 wxArrayInt tabArray;
6799 if (textAttr.GetTabs().IsEmpty())
6800 tabArray = wxRichTextParagraph::GetDefaultTabs();
6801 else
6802 tabArray = textAttr.GetTabs();
6803
6804 int tabCount = tabArray.GetCount();
6805
6806 for (int i = 0; i < tabCount; ++i)
6807 {
6808 int pos = tabArray[i];
6809 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6810 tabArray[i] = pos;
6811 }
6812
6813 int nextTabPos = -1;
6814
6815 while (stringChunk.Find(wxT('\t')) >= 0)
6816 {
6817 int absoluteWidth = 0;
6818
6819 // the string has a tab
6820 // break up the string at the Tab
6821 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6822 stringChunk = stringChunk.AfterFirst(wxT('\t'));
6823
6824 if (partialExtents)
6825 {
6826 int oldWidth;
6827 if (partialExtents->GetCount() > 0)
6828 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6829 else
6830 oldWidth = 0;
6831
6832 // Add these partial extents
6833 wxArrayInt p;
6834 dc.GetPartialTextExtents(stringFragment, p);
6835 size_t j;
6836 for (j = 0; j < p.GetCount(); j++)
6837 partialExtents->Add(oldWidth + p[j]);
6838
6839 if (partialExtents->GetCount() > 0)
6840 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
6841 else
6842 absoluteWidth = relativeX;
6843 }
6844 else
6845 {
6846 dc.GetTextExtent(stringFragment, & w, & h);
6847 width += w;
6848 absoluteWidth = width + relativeX;
6849 haveDescent = true;
6850 }
6851
6852 bool notFound = true;
6853 for (int i = 0; i < tabCount && notFound; ++i)
6854 {
6855 nextTabPos = tabArray.Item(i);
6856
6857 // Find the next tab position.
6858 // Even if we're at the end of the tab array, we must still process the chunk.
6859
6860 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
6861 {
6862 if (nextTabPos <= absoluteWidth)
6863 {
6864 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6865 nextTabPos = absoluteWidth + defaultTabWidth;
6866 }
6867
6868 notFound = false;
6869 width = nextTabPos - relativeX;
6870
6871 if (partialExtents)
6872 partialExtents->Add(width);
6873 }
6874 }
6875 }
6876 }
6877
6878 if (!stringChunk.IsEmpty())
6879 {
6880 if (partialExtents)
6881 {
6882 int oldWidth;
6883 if (partialExtents->GetCount() > 0)
6884 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6885 else
6886 oldWidth = 0;
6887
6888 // Add these partial extents
6889 wxArrayInt p;
6890 dc.GetPartialTextExtents(stringChunk, p);
6891 size_t j;
6892 for (j = 0; j < p.GetCount(); j++)
6893 partialExtents->Add(oldWidth + p[j]);
6894 }
6895 else
6896 {
6897 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6898 width += w;
6899 haveDescent = true;
6900 }
6901 }
6902
6903 if (partialExtents)
6904 {
6905 int charHeight = dc.GetCharHeight();
6906 if ((*partialExtents).GetCount() > 0)
6907 w = (*partialExtents)[partialExtents->GetCount()-1];
6908 else
6909 w = 0;
6910 size = wxSize(w, charHeight);
6911 }
6912 else
6913 {
6914 size = wxSize(width, dc.GetCharHeight());
6915 }
6916
6917 if (!haveDescent)
6918 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6919
6920 if ( bScript )
6921 dc.SetFont(font);
6922
6923 return true;
6924 }
6925
6926 /// Do a split, returning an object containing the second part, and setting
6927 /// the first part in 'this'.
6928 wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6929 {
6930 long index = pos - GetRange().GetStart();
6931
6932 if (index < 0 || index >= (int) m_text.length())
6933 return NULL;
6934
6935 wxString firstPart = m_text.Mid(0, index);
6936 wxString secondPart = m_text.Mid(index);
6937
6938 m_text = firstPart;
6939
6940 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6941 newObject->SetAttributes(GetAttributes());
6942 newObject->SetProperties(GetProperties());
6943
6944 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6945 GetRange().SetEnd(pos-1);
6946
6947 return newObject;
6948 }
6949
6950 /// Calculate range
6951 void wxRichTextPlainText::CalculateRange(long start, long& end)
6952 {
6953 end = start + m_text.length() - 1;
6954 m_range.SetRange(start, end);
6955 }
6956
6957 /// Delete range
6958 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6959 {
6960 wxRichTextRange r = range;
6961
6962 r.LimitTo(GetRange());
6963
6964 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6965 {
6966 m_text.Empty();
6967 return true;
6968 }
6969
6970 long startIndex = r.GetStart() - GetRange().GetStart();
6971 long len = r.GetLength();
6972
6973 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6974 return true;
6975 }
6976
6977 /// Get text for the given range.
6978 wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6979 {
6980 wxRichTextRange r = range;
6981
6982 r.LimitTo(GetRange());
6983
6984 long startIndex = r.GetStart() - GetRange().GetStart();
6985 long len = r.GetLength();
6986
6987 return m_text.Mid(startIndex, len);
6988 }
6989
6990 /// Returns true if this object can merge itself with the given one.
6991 bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
6992 {
6993 // JACS 2013-01-27
6994 if (!context.GetVirtualAttributesEnabled())
6995 {
6996 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
6997 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
6998 }
6999 else
7000 {
7001 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7002 if (!otherObj || m_text.empty())
7003 return false;
7004
7005 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7006 return false;
7007
7008 // Check if differing virtual attributes makes it impossible to merge
7009 // these strings.
7010
7011 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7012 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7013 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7014 return true;
7015 else if (hasVirtualAttr1 != hasVirtualAttr2)
7016 return false;
7017 else
7018 {
7019 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7020 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7021 return virtualAttr1 == virtualAttr2;
7022 }
7023 }
7024 }
7025
7026 /// Returns true if this object merged itself with the given one.
7027 /// The calling code will then delete the given object.
7028 bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
7029 {
7030 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7031 wxASSERT( textObject != NULL );
7032
7033 if (textObject)
7034 {
7035 m_text += textObject->GetText();
7036 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
7037 return true;
7038 }
7039 else
7040 return false;
7041 }
7042
7043 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7044 {
7045 // If this object has any virtual attributes at all, whether for the whole object
7046 // or individual ones, we should try splitting it by calling Split.
7047 // Must be more than one character in order to be able to split.
7048 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7049 }
7050
7051 wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7052 {
7053 int count = context.GetVirtualSubobjectAttributesCount(this);
7054 if (count > 0 && GetParent())
7055 {
7056 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7057 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7058 if (node)
7059 {
7060 const wxRichTextAttr emptyAttr;
7061 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7062
7063 wxArrayInt positions;
7064 wxRichTextAttrArray attributes;
7065 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7066 {
7067 wxASSERT(positions.GetCount() == attributes.GetCount());
7068
7069 // We will gather up runs of text with the same virtual attributes
7070
7071 int len = m_text.Length();
7072 int i = 0;
7073
7074 // runStart and runEnd represent the accumulated run with a consistent attribute
7075 // that hasn't yet been appended
7076 int runStart = -1;
7077 int runEnd = -1;
7078 wxRichTextAttr currentAttr;
7079 wxString text = m_text;
7080 wxRichTextPlainText* lastPlainText = this;
7081
7082 for (i = 0; i < (int) positions.GetCount(); i++)
7083 {
7084 int pos = positions[i];
7085 wxASSERT(pos >= 0 && pos < len);
7086 if (pos >= 0 && pos < len)
7087 {
7088 const wxRichTextAttr& attr = attributes[i];
7089
7090 if (pos == 0)
7091 {
7092 runStart = 0;
7093 currentAttr = attr;
7094 }
7095 // Check if there was a gap from the last known attribute and this.
7096 // In that case, we need to do something with the span of non-attributed text.
7097 else if ((pos-1) > runEnd)
7098 {
7099 if (runEnd == -1)
7100 {
7101 // We hadn't processed anything previously, so the previous run is from the text start
7102 // to just before this position. The current attribute remains empty.
7103 runStart = 0;
7104 runEnd = pos-1;
7105 }
7106 else
7107 {
7108 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7109 // then just extend the run.
7110 if (currentAttr.IsDefault())
7111 {
7112 runEnd = pos-1;
7113 }
7114 else
7115 {
7116 // We need to add an object, or reuse the existing one.
7117 if (runStart == 0)
7118 {
7119 lastPlainText = this;
7120 SetText(text.Mid(runStart, runEnd - runStart + 1));
7121 }
7122 else
7123 {
7124 wxRichTextPlainText* obj = new wxRichTextPlainText;
7125 lastPlainText = obj;
7126 obj->SetAttributes(GetAttributes());
7127 obj->SetProperties(GetProperties());
7128 obj->SetParent(parent);
7129
7130 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7131 if (next)
7132 parent->GetChildren().Insert(next, obj);
7133 else
7134 parent->GetChildren().Append(obj);
7135 }
7136
7137 runStart = runEnd+1;
7138 runEnd = pos-1;
7139
7140 currentAttr = emptyAttr;
7141 }
7142 }
7143 }
7144
7145 wxASSERT(runEnd == pos-1);
7146
7147 // Now we only have to deal with the previous run
7148 if (currentAttr == attr)
7149 {
7150 // If we still have the same attributes, then we
7151 // simply increase the run size.
7152 runEnd = pos;
7153 }
7154 else
7155 {
7156 if (runEnd >= 0)
7157 {
7158 // We need to add an object, or reuse the existing one.
7159 if (runStart == 0)
7160 {
7161 lastPlainText = this;
7162 SetText(text.Mid(runStart, runEnd - runStart + 1));
7163 }
7164 else
7165 {
7166 wxRichTextPlainText* obj = new wxRichTextPlainText;
7167 lastPlainText = obj;
7168 obj->SetAttributes(GetAttributes());
7169 obj->SetProperties(GetProperties());
7170 obj->SetParent(parent);
7171
7172 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7173 if (next)
7174 parent->GetChildren().Insert(next, obj);
7175 else
7176 parent->GetChildren().Append(obj);
7177 }
7178 }
7179
7180 runStart = pos;
7181 runEnd = pos;
7182
7183 currentAttr = attr;
7184 }
7185 }
7186 }
7187
7188 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7189 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7190 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7191 {
7192 // If the current attribute is empty, merge the run with the next fragment
7193 // which by definition (because it's not specified) has empty attributes.
7194 if (currentAttr.IsDefault())
7195 runEnd = (len-1);
7196
7197 if (runEnd < (len-1))
7198 {
7199 // We need to add an object, or reuse the existing one.
7200 if (runStart == 0)
7201 {
7202 lastPlainText = this;
7203 SetText(text.Mid(runStart, runEnd - runStart + 1));
7204 }
7205 else
7206 {
7207 wxRichTextPlainText* obj = new wxRichTextPlainText;
7208 lastPlainText = obj;
7209 obj->SetAttributes(GetAttributes());
7210 obj->SetProperties(GetProperties());
7211 obj->SetParent(parent);
7212
7213 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7214 if (next)
7215 parent->GetChildren().Insert(next, obj);
7216 else
7217 parent->GetChildren().Append(obj);
7218 }
7219
7220 runStart = runEnd+1;
7221 runEnd = (len-1);
7222 }
7223
7224 // Now the last, non-attributed fragment at the end, if any
7225 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7226 {
7227 wxASSERT(runStart != 0);
7228
7229 wxRichTextPlainText* obj = new wxRichTextPlainText;
7230 obj->SetAttributes(GetAttributes());
7231 obj->SetProperties(GetProperties());
7232 obj->SetParent(parent);
7233
7234 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7235 if (next)
7236 parent->GetChildren().Insert(next, obj);
7237 else
7238 parent->GetChildren().Append(obj);
7239
7240 lastPlainText = obj;
7241 }
7242 }
7243
7244 return lastPlainText;
7245 }
7246 }
7247 }
7248 return this;
7249 }
7250
7251 /// Dump to output stream for debugging
7252 void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7253 {
7254 wxRichTextObject::Dump(stream);
7255 stream << m_text << wxT("\n");
7256 }
7257
7258 /// Get the first position from pos that has a line break character.
7259 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7260 {
7261 int i;
7262 int len = m_text.length();
7263 int startPos = pos - m_range.GetStart();
7264 for (i = startPos; i < len; i++)
7265 {
7266 wxChar ch = m_text[i];
7267 if (ch == wxRichTextLineBreakChar)
7268 {
7269 return i + m_range.GetStart();
7270 }
7271 }
7272 return -1;
7273 }
7274
7275 /*!
7276 * wxRichTextBuffer
7277 * This is a kind of box, used to represent the whole buffer
7278 */
7279
7280 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7281
7282 wxList wxRichTextBuffer::sm_handlers;
7283 wxList wxRichTextBuffer::sm_drawingHandlers;
7284 wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7285 wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7286 int wxRichTextBuffer::sm_bulletRightMargin = 20;
7287 float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
7288 bool wxRichTextBuffer::sm_floatingLayoutMode = true;
7289
7290 /// Initialisation
7291 void wxRichTextBuffer::Init()
7292 {
7293 m_commandProcessor = new wxCommandProcessor;
7294 m_styleSheet = NULL;
7295 m_modified = false;
7296 m_batchedCommandDepth = 0;
7297 m_batchedCommand = NULL;
7298 m_suppressUndo = 0;
7299 m_handlerFlags = 0;
7300 m_scale = 1.0;
7301 m_dimensionScale = 1.0;
7302 m_fontScale = 1.0;
7303 SetMargins(4);
7304 }
7305
7306 /// Initialisation
7307 wxRichTextBuffer::~wxRichTextBuffer()
7308 {
7309 delete m_commandProcessor;
7310 delete m_batchedCommand;
7311
7312 ClearStyleStack();
7313 ClearEventHandlers();
7314 }
7315
7316 void wxRichTextBuffer::ResetAndClearCommands()
7317 {
7318 Reset();
7319
7320 GetCommandProcessor()->ClearCommands();
7321
7322 Modify(false);
7323 Invalidate(wxRICHTEXT_ALL);
7324 }
7325
7326 void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7327 {
7328 wxRichTextParagraphLayoutBox::Copy(obj);
7329
7330 m_styleSheet = obj.m_styleSheet;
7331 m_modified = obj.m_modified;
7332 m_batchedCommandDepth = 0;
7333 if (m_batchedCommand)
7334 delete m_batchedCommand;
7335 m_batchedCommand = NULL;
7336 m_suppressUndo = obj.m_suppressUndo;
7337 m_invalidRange = obj.m_invalidRange;
7338 m_dimensionScale = obj.m_dimensionScale;
7339 m_fontScale = obj.m_fontScale;
7340 }
7341
7342 /// Push style sheet to top of stack
7343 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7344 {
7345 if (m_styleSheet)
7346 styleSheet->InsertSheet(m_styleSheet);
7347
7348 SetStyleSheet(styleSheet);
7349
7350 return true;
7351 }
7352
7353 /// Pop style sheet from top of stack
7354 wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7355 {
7356 if (m_styleSheet)
7357 {
7358 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7359 m_styleSheet = oldSheet->GetNextSheet();
7360 oldSheet->Unlink();
7361
7362 return oldSheet;
7363 }
7364 else
7365 return NULL;
7366 }
7367
7368 /// Submit command to insert paragraphs
7369 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7370 {
7371 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
7372 }
7373
7374 /// Submit command to insert paragraphs
7375 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
7376 {
7377 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7378
7379 action->GetNewParagraphs() = paragraphs;
7380
7381 action->SetPosition(pos);
7382
7383 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
7384 if (!paragraphs.GetPartialParagraph())
7385 range.SetEnd(range.GetEnd()+1);
7386
7387 // Set the range we'll need to delete in Undo
7388 action->SetRange(range);
7389
7390 buffer->SubmitAction(action);
7391
7392 return true;
7393 }
7394
7395 /// Submit command to insert the given text
7396 bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7397 {
7398 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
7399 }
7400
7401 /// Submit command to insert the given text
7402 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7403 {
7404 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7405
7406 wxRichTextAttr* p = NULL;
7407 wxRichTextAttr paraAttr;
7408 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7409 {
7410 // Get appropriate paragraph style
7411 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
7412 if (!paraAttr.IsDefault())
7413 p = & paraAttr;
7414 }
7415
7416 action->GetNewParagraphs().AddParagraphs(text, p);
7417
7418 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
7419
7420 if (!text.empty() && text.Last() != wxT('\n'))
7421 {
7422 // Don't count the newline when undoing
7423 length --;
7424 action->GetNewParagraphs().SetPartialParagraph(true);
7425 }
7426 else if (!text.empty() && text.Last() == wxT('\n'))
7427 length --;
7428
7429 action->SetPosition(pos);
7430
7431 // Set the range we'll need to delete in Undo
7432 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7433
7434 buffer->SubmitAction(action);
7435
7436 return true;
7437 }
7438
7439 /// Submit command to insert the given text
7440 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
7441 {
7442 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
7443 }
7444
7445 /// Submit command to insert the given text
7446 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
7447 {
7448 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7449
7450 wxRichTextAttr* p = NULL;
7451 wxRichTextAttr paraAttr;
7452 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7453 {
7454 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
7455 if (!paraAttr.IsDefault())
7456 p = & paraAttr;
7457 }
7458
7459 wxRichTextAttr attr(buffer->GetDefaultStyle());
7460 // Don't include box attributes such as margins
7461 attr.GetTextBoxAttr().Reset();
7462
7463 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
7464 action->GetNewParagraphs().AppendChild(newPara);
7465 action->GetNewParagraphs().UpdateRanges();
7466 action->GetNewParagraphs().SetPartialParagraph(false);
7467 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7468 long pos1 = pos;
7469
7470 if (p)
7471 newPara->SetAttributes(*p);
7472
7473 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7474 {
7475 if (para && para->GetRange().GetEnd() == pos)
7476 pos1 ++;
7477
7478 // Now see if we need to number the paragraph.
7479 if (newPara->GetAttributes().HasBulletNumber())
7480 {
7481 wxRichTextAttr numberingAttr;
7482 if (FindNextParagraphNumber(para, numberingAttr))
7483 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7484 }
7485 }
7486
7487 action->SetPosition(pos);
7488
7489 // Use the default character style
7490 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
7491 {
7492 // Check whether the default style merely reflects the paragraph/basic style,
7493 // in which case don't apply it.
7494 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
7495 defaultStyle.GetTextBoxAttr().Reset();
7496 wxRichTextAttr toApply;
7497 if (para)
7498 {
7499 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
7500 wxRichTextAttr newAttr;
7501 // This filters out attributes that are accounted for by the current
7502 // paragraph/basic style
7503 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7504 }
7505 else
7506 toApply = defaultStyle;
7507
7508 if (!toApply.IsDefault())
7509 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7510 }
7511
7512 // Set the range we'll need to delete in Undo
7513 action->SetRange(wxRichTextRange(pos1, pos1));
7514
7515 buffer->SubmitAction(action);
7516
7517 return true;
7518 }
7519
7520 /// Submit command to insert the given image
7521 bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7522 const wxRichTextAttr& textAttr)
7523 {
7524 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
7525 }
7526
7527 /// Submit command to insert the given image
7528 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7529 wxRichTextCtrl* ctrl, int flags,
7530 const wxRichTextAttr& textAttr)
7531 {
7532 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7533
7534 wxRichTextAttr* p = NULL;
7535 wxRichTextAttr paraAttr;
7536 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7537 {
7538 paraAttr = GetStyleForNewParagraph(buffer, pos);
7539 if (!paraAttr.IsDefault())
7540 p = & paraAttr;
7541 }
7542
7543 wxRichTextAttr attr(buffer->GetDefaultStyle());
7544
7545 // Don't include box attributes such as margins
7546 attr.GetTextBoxAttr().Reset();
7547
7548 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7549 if (p)
7550 newPara->SetAttributes(*p);
7551
7552 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7553 newPara->AppendChild(imageObject);
7554 imageObject->SetAttributes(textAttr);
7555 action->GetNewParagraphs().AppendChild(newPara);
7556 action->GetNewParagraphs().UpdateRanges();
7557
7558 action->GetNewParagraphs().SetPartialParagraph(true);
7559
7560 action->SetPosition(pos);
7561
7562 // Set the range we'll need to delete in Undo
7563 action->SetRange(wxRichTextRange(pos, pos));
7564
7565 buffer->SubmitAction(action);
7566
7567 return true;
7568 }
7569
7570 // Insert an object with no change of it
7571 wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7572 {
7573 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
7574 }
7575
7576 // Insert an object with no change of it
7577 wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7578 {
7579 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7580
7581 wxRichTextAttr* p = NULL;
7582 wxRichTextAttr paraAttr;
7583 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7584 {
7585 paraAttr = GetStyleForNewParagraph(buffer, pos);
7586 if (!paraAttr.IsDefault())
7587 p = & paraAttr;
7588 }
7589
7590 wxRichTextAttr attr(buffer->GetDefaultStyle());
7591
7592 // Don't include box attributes such as margins
7593 attr.GetTextBoxAttr().Reset();
7594
7595 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7596 if (p)
7597 newPara->SetAttributes(*p);
7598
7599 newPara->AppendChild(object);
7600 action->GetNewParagraphs().AppendChild(newPara);
7601 action->GetNewParagraphs().UpdateRanges();
7602
7603 action->GetNewParagraphs().SetPartialParagraph(true);
7604
7605 action->SetPosition(pos);
7606
7607 // Set the range we'll need to delete in Undo
7608 action->SetRange(wxRichTextRange(pos, pos));
7609
7610 buffer->SubmitAction(action);
7611
7612 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7613 return obj;
7614 }
7615
7616 wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7617 const wxRichTextProperties& properties,
7618 wxRichTextCtrl* ctrl, int flags,
7619 const wxRichTextAttr& textAttr)
7620 {
7621 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7622
7623 wxRichTextAttr* p = NULL;
7624 wxRichTextAttr paraAttr;
7625 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7626 {
7627 paraAttr = GetStyleForNewParagraph(buffer, pos);
7628 if (!paraAttr.IsDefault())
7629 p = & paraAttr;
7630 }
7631
7632 wxRichTextAttr attr(buffer->GetDefaultStyle());
7633
7634 // Don't include box attributes such as margins
7635 attr.GetTextBoxAttr().Reset();
7636
7637 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7638 if (p)
7639 newPara->SetAttributes(*p);
7640
7641 wxRichTextField* fieldObject = new wxRichTextField();
7642 fieldObject->wxRichTextObject::SetProperties(properties);
7643 fieldObject->SetFieldType(fieldType);
7644 fieldObject->SetAttributes(textAttr);
7645 newPara->AppendChild(fieldObject);
7646 action->GetNewParagraphs().AppendChild(newPara);
7647 action->GetNewParagraphs().UpdateRanges();
7648 action->GetNewParagraphs().SetPartialParagraph(true);
7649 action->SetPosition(pos);
7650
7651 // Set the range we'll need to delete in Undo
7652 action->SetRange(wxRichTextRange(pos, pos));
7653
7654 buffer->SubmitAction(action);
7655
7656 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7657 return obj;
7658 }
7659
7660 /// Get the style that is appropriate for a new paragraph at this position.
7661 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7662 /// style.
7663 wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
7664 {
7665 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7666 if (para)
7667 {
7668 wxRichTextAttr attr;
7669 bool foundAttributes = false;
7670
7671 // Look for a matching paragraph style
7672 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
7673 {
7674 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
7675 if (paraDef)
7676 {
7677 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7678 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
7679 {
7680 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
7681 if (nextParaDef)
7682 {
7683 foundAttributes = true;
7684 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7685 }
7686 }
7687
7688 // If we didn't find the 'next style', use this style instead.
7689 if (!foundAttributes)
7690 {
7691 foundAttributes = true;
7692 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7693 }
7694 }
7695 }
7696
7697 // Also apply list style if present
7698 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
7699 {
7700 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
7701 if (listDef)
7702 {
7703 int thisIndent = para->GetAttributes().GetLeftIndent();
7704 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7705
7706 // Apply the overall list style, and item style for this level
7707 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
7708 wxRichTextApplyStyle(attr, listStyle);
7709 attr.SetOutlineLevel(thisLevel);
7710 if (para->GetAttributes().HasBulletNumber())
7711 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7712 }
7713 }
7714
7715 if (!foundAttributes)
7716 {
7717 attr = para->GetAttributes();
7718 int flags = attr.GetFlags();
7719
7720 // Eliminate character styles
7721 flags &= ( (~ wxTEXT_ATTR_FONT) |
7722 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7723 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
7724 attr.SetFlags(flags);
7725 }
7726
7727 return attr;
7728 }
7729 else
7730 return wxRichTextAttr();
7731 }
7732
7733 /// Submit command to delete this range
7734 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
7735 {
7736 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7737 }
7738
7739 /// Submit command to delete this range
7740 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7741 {
7742 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7743
7744 action->SetPosition(ctrl->GetCaretPosition());
7745
7746 // Set the range to delete
7747 action->SetRange(range);
7748
7749 // Copy the fragment that we'll need to restore in Undo
7750 CopyFragment(range, action->GetOldParagraphs());
7751
7752 // See if we're deleting a paragraph marker, in which case we need to
7753 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7754 if (range.GetStart() == range.GetEnd())
7755 {
7756 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7757 if (para && para->GetRange().GetEnd() == range.GetEnd())
7758 {
7759 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7760 if (nextPara && nextPara != para)
7761 {
7762 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7763 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
7764 }
7765 }
7766 }
7767
7768 buffer->SubmitAction(action);
7769
7770 return true;
7771 }
7772
7773 /// Collapse undo/redo commands
7774 bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7775 {
7776 if (m_batchedCommandDepth == 0)
7777 {
7778 wxASSERT(m_batchedCommand == NULL);
7779 if (m_batchedCommand)
7780 {
7781 GetCommandProcessor()->Store(m_batchedCommand);
7782 }
7783 m_batchedCommand = new wxRichTextCommand(cmdName);
7784 }
7785
7786 m_batchedCommandDepth ++;
7787
7788 return true;
7789 }
7790
7791 /// Collapse undo/redo commands
7792 bool wxRichTextBuffer::EndBatchUndo()
7793 {
7794 m_batchedCommandDepth --;
7795
7796 wxASSERT(m_batchedCommandDepth >= 0);
7797 wxASSERT(m_batchedCommand != NULL);
7798
7799 if (m_batchedCommandDepth == 0)
7800 {
7801 GetCommandProcessor()->Store(m_batchedCommand);
7802 m_batchedCommand = NULL;
7803 }
7804
7805 return true;
7806 }
7807
7808 /// Submit immediately, or delay according to whether collapsing is on
7809 bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7810 {
7811 if (action && !action->GetNewParagraphs().IsEmpty())
7812 PrepareContent(action->GetNewParagraphs());
7813
7814 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
7815 {
7816 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7817 cmd->AddAction(action);
7818 cmd->Do();
7819 cmd->GetActions().Clear();
7820 delete cmd;
7821
7822 m_batchedCommand->AddAction(action);
7823 }
7824 else
7825 {
7826 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7827 cmd->AddAction(action);
7828
7829 // Only store it if we're not suppressing undo.
7830 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7831 }
7832
7833 return true;
7834 }
7835
7836 /// Begin suppressing undo/redo commands.
7837 bool wxRichTextBuffer::BeginSuppressUndo()
7838 {
7839 m_suppressUndo ++;
7840
7841 return true;
7842 }
7843
7844 /// End suppressing undo/redo commands.
7845 bool wxRichTextBuffer::EndSuppressUndo()
7846 {
7847 m_suppressUndo --;
7848
7849 return true;
7850 }
7851
7852 /// Begin using a style
7853 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
7854 {
7855 wxRichTextAttr newStyle(GetDefaultStyle());
7856 newStyle.GetTextBoxAttr().Reset();
7857
7858 // Save the old default style
7859 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
7860
7861 wxRichTextApplyStyle(newStyle, style);
7862 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7863
7864 SetDefaultStyle(newStyle);
7865
7866 return true;
7867 }
7868
7869 /// End the style
7870 bool wxRichTextBuffer::EndStyle()
7871 {
7872 if (!m_attributeStack.GetFirst())
7873 {
7874 wxLogDebug(_("Too many EndStyle calls!"));
7875 return false;
7876 }
7877
7878 wxList::compatibility_iterator node = m_attributeStack.GetLast();
7879 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
7880 m_attributeStack.Erase(node);
7881
7882 SetDefaultStyle(*attr);
7883
7884 delete attr;
7885 return true;
7886 }
7887
7888 /// End all styles
7889 bool wxRichTextBuffer::EndAllStyles()
7890 {
7891 while (m_attributeStack.GetCount() != 0)
7892 EndStyle();
7893 return true;
7894 }
7895
7896 /// Clear the style stack
7897 void wxRichTextBuffer::ClearStyleStack()
7898 {
7899 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
7900 delete (wxRichTextAttr*) node->GetData();
7901 m_attributeStack.Clear();
7902 }
7903
7904 /// Begin using bold
7905 bool wxRichTextBuffer::BeginBold()
7906 {
7907 wxRichTextAttr attr;
7908 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7909
7910 return BeginStyle(attr);
7911 }
7912
7913 /// Begin using italic
7914 bool wxRichTextBuffer::BeginItalic()
7915 {
7916 wxRichTextAttr attr;
7917 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7918
7919 return BeginStyle(attr);
7920 }
7921
7922 /// Begin using underline
7923 bool wxRichTextBuffer::BeginUnderline()
7924 {
7925 wxRichTextAttr attr;
7926 attr.SetFontUnderlined(true);
7927
7928 return BeginStyle(attr);
7929 }
7930
7931 /// Begin using point size
7932 bool wxRichTextBuffer::BeginFontSize(int pointSize)
7933 {
7934 wxRichTextAttr attr;
7935 attr.SetFontSize(pointSize);
7936
7937 return BeginStyle(attr);
7938 }
7939
7940 /// Begin using this font
7941 bool wxRichTextBuffer::BeginFont(const wxFont& font)
7942 {
7943 wxRichTextAttr attr;
7944 attr.SetFont(font);
7945
7946 return BeginStyle(attr);
7947 }
7948
7949 /// Begin using this colour
7950 bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7951 {
7952 wxRichTextAttr attr;
7953 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7954 attr.SetTextColour(colour);
7955
7956 return BeginStyle(attr);
7957 }
7958
7959 /// Begin using alignment
7960 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7961 {
7962 wxRichTextAttr attr;
7963 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7964 attr.SetAlignment(alignment);
7965
7966 return BeginStyle(attr);
7967 }
7968
7969 /// Begin left indent
7970 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7971 {
7972 wxRichTextAttr attr;
7973 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7974 attr.SetLeftIndent(leftIndent, leftSubIndent);
7975
7976 return BeginStyle(attr);
7977 }
7978
7979 /// Begin right indent
7980 bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7981 {
7982 wxRichTextAttr attr;
7983 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7984 attr.SetRightIndent(rightIndent);
7985
7986 return BeginStyle(attr);
7987 }
7988
7989 /// Begin paragraph spacing
7990 bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7991 {
7992 long flags = 0;
7993 if (before != 0)
7994 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7995 if (after != 0)
7996 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7997
7998 wxRichTextAttr attr;
7999 attr.SetFlags(flags);
8000 attr.SetParagraphSpacingBefore(before);
8001 attr.SetParagraphSpacingAfter(after);
8002
8003 return BeginStyle(attr);
8004 }
8005
8006 /// Begin line spacing
8007 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8008 {
8009 wxRichTextAttr attr;
8010 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8011 attr.SetLineSpacing(lineSpacing);
8012
8013 return BeginStyle(attr);
8014 }
8015
8016 /// Begin numbered bullet
8017 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8018 {
8019 wxRichTextAttr attr;
8020 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8021 attr.SetBulletStyle(bulletStyle);
8022 attr.SetBulletNumber(bulletNumber);
8023 attr.SetLeftIndent(leftIndent, leftSubIndent);
8024
8025 return BeginStyle(attr);
8026 }
8027
8028 /// Begin symbol bullet
8029 bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
8030 {
8031 wxRichTextAttr attr;
8032 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8033 attr.SetBulletStyle(bulletStyle);
8034 attr.SetLeftIndent(leftIndent, leftSubIndent);
8035 attr.SetBulletText(symbol);
8036
8037 return BeginStyle(attr);
8038 }
8039
8040 /// Begin standard bullet
8041 bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8042 {
8043 wxRichTextAttr attr;
8044 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8045 attr.SetBulletStyle(bulletStyle);
8046 attr.SetLeftIndent(leftIndent, leftSubIndent);
8047 attr.SetBulletName(bulletName);
8048
8049 return BeginStyle(attr);
8050 }
8051
8052 /// Begin named character style
8053 bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8054 {
8055 if (GetStyleSheet())
8056 {
8057 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8058 if (def)
8059 {
8060 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8061 return BeginStyle(attr);
8062 }
8063 }
8064 return false;
8065 }
8066
8067 /// Begin named paragraph style
8068 bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8069 {
8070 if (GetStyleSheet())
8071 {
8072 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8073 if (def)
8074 {
8075 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8076 return BeginStyle(attr);
8077 }
8078 }
8079 return false;
8080 }
8081
8082 /// Begin named list style
8083 bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8084 {
8085 if (GetStyleSheet())
8086 {
8087 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8088 if (def)
8089 {
8090 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
8091
8092 attr.SetBulletNumber(number);
8093
8094 return BeginStyle(attr);
8095 }
8096 }
8097 return false;
8098 }
8099
8100 /// Begin URL
8101 bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8102 {
8103 wxRichTextAttr attr;
8104
8105 if (!characterStyle.IsEmpty() && GetStyleSheet())
8106 {
8107 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8108 if (def)
8109 {
8110 attr = def->GetStyleMergedWithBase(GetStyleSheet());
8111 }
8112 }
8113 attr.SetURL(url);
8114
8115 return BeginStyle(attr);
8116 }
8117
8118 /// Adds a handler to the end
8119 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8120 {
8121 sm_handlers.Append(handler);
8122 }
8123
8124 /// Inserts a handler at the front
8125 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8126 {
8127 sm_handlers.Insert( handler );
8128 }
8129
8130 /// Removes a handler
8131 bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8132 {
8133 wxRichTextFileHandler *handler = FindHandler(name);
8134 if (handler)
8135 {
8136 sm_handlers.DeleteObject(handler);
8137 delete handler;
8138 return true;
8139 }
8140 else
8141 return false;
8142 }
8143
8144 /// Finds a handler by filename or, if supplied, type
8145 wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8146 wxRichTextFileType imageType)
8147 {
8148 if (imageType != wxRICHTEXT_TYPE_ANY)
8149 return FindHandler(imageType);
8150 else if (!filename.IsEmpty())
8151 {
8152 wxString path, file, ext;
8153 wxFileName::SplitPath(filename, & path, & file, & ext);
8154 return FindHandler(ext, imageType);
8155 }
8156 else
8157 return NULL;
8158 }
8159
8160
8161 /// Finds a handler by name
8162 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8163 {
8164 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8165 while (node)
8166 {
8167 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8168 if (handler->GetName().Lower() == name.Lower()) return handler;
8169
8170 node = node->GetNext();
8171 }
8172 return NULL;
8173 }
8174
8175 /// Finds a handler by extension and type
8176 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
8177 {
8178 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8179 while (node)
8180 {
8181 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8182 if ( handler->GetExtension().Lower() == extension.Lower() &&
8183 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8184 return handler;
8185 node = node->GetNext();
8186 }
8187 return 0;
8188 }
8189
8190 /// Finds a handler by type
8191 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
8192 {
8193 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8194 while (node)
8195 {
8196 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8197 if (handler->GetType() == type) return handler;
8198 node = node->GetNext();
8199 }
8200 return NULL;
8201 }
8202
8203 void wxRichTextBuffer::InitStandardHandlers()
8204 {
8205 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8206 AddHandler(new wxRichTextPlainTextHandler);
8207 }
8208
8209 void wxRichTextBuffer::CleanUpHandlers()
8210 {
8211 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8212 while (node)
8213 {
8214 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8215 wxList::compatibility_iterator next = node->GetNext();
8216 delete handler;
8217 node = next;
8218 }
8219
8220 sm_handlers.Clear();
8221 }
8222
8223 wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
8224 {
8225 if (types)
8226 types->Clear();
8227
8228 wxString wildcard;
8229
8230 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8231 int count = 0;
8232 while (node)
8233 {
8234 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
8235 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
8236 {
8237 if (combine)
8238 {
8239 if (count > 0)
8240 wildcard += wxT(";");
8241 wildcard += wxT("*.") + handler->GetExtension();
8242 }
8243 else
8244 {
8245 if (count > 0)
8246 wildcard += wxT("|");
8247 wildcard += handler->GetName();
8248 wildcard += wxT(" ");
8249 wildcard += _("files");
8250 wildcard += wxT(" (*.");
8251 wildcard += handler->GetExtension();
8252 wildcard += wxT(")|*.");
8253 wildcard += handler->GetExtension();
8254 if (types)
8255 types->Add(handler->GetType());
8256 }
8257 count ++;
8258 }
8259
8260 node = node->GetNext();
8261 }
8262
8263 if (combine)
8264 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8265 return wildcard;
8266 }
8267
8268 /// Load a file
8269 bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
8270 {
8271 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8272 if (handler)
8273 {
8274 SetDefaultStyle(wxRichTextAttr());
8275 handler->SetFlags(GetHandlerFlags());
8276 bool success = handler->LoadFile(this, filename);
8277 Invalidate(wxRICHTEXT_ALL);
8278 return success;
8279 }
8280 else
8281 return false;
8282 }
8283
8284 /// Save a file
8285 bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
8286 {
8287 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8288 if (handler)
8289 {
8290 handler->SetFlags(GetHandlerFlags());
8291 return handler->SaveFile(this, filename);
8292 }
8293 else
8294 return false;
8295 }
8296
8297 /// Load from a stream
8298 bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
8299 {
8300 wxRichTextFileHandler* handler = FindHandler(type);
8301 if (handler)
8302 {
8303 SetDefaultStyle(wxRichTextAttr());
8304 handler->SetFlags(GetHandlerFlags());
8305 bool success = handler->LoadFile(this, stream);
8306 Invalidate(wxRICHTEXT_ALL);
8307 return success;
8308 }
8309 else
8310 return false;
8311 }
8312
8313 /// Save to a stream
8314 bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
8315 {
8316 wxRichTextFileHandler* handler = FindHandler(type);
8317 if (handler)
8318 {
8319 handler->SetFlags(GetHandlerFlags());
8320 return handler->SaveFile(this, stream);
8321 }
8322 else
8323 return false;
8324 }
8325
8326 /// Copy the range to the clipboard
8327 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8328 {
8329 bool success = false;
8330 wxRichTextParagraphLayoutBox* container = this;
8331 if (GetRichTextCtrl())
8332 container = GetRichTextCtrl()->GetFocusObject();
8333
8334 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8335
8336 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8337 {
8338 wxTheClipboard->Clear();
8339
8340 // Add composite object
8341
8342 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8343
8344 {
8345 wxString text = container->GetTextForRange(range);
8346
8347 #ifdef __WXMSW__
8348 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8349 #endif
8350
8351 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8352 }
8353
8354 // Add rich text buffer data object. This needs the XML handler to be present.
8355
8356 if (FindHandler(wxRICHTEXT_TYPE_XML))
8357 {
8358 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
8359 container->CopyFragment(range, *richTextBuf);
8360
8361 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8362 }
8363
8364 if (wxTheClipboard->SetData(compositeObject))
8365 success = true;
8366
8367 wxTheClipboard->Close();
8368 }
8369
8370 #else
8371 wxUnusedVar(range);
8372 #endif
8373 return success;
8374 }
8375
8376 /// Paste the clipboard content to the buffer
8377 bool wxRichTextBuffer::PasteFromClipboard(long position)
8378 {
8379 bool success = false;
8380 wxRichTextParagraphLayoutBox* container = this;
8381 if (GetRichTextCtrl())
8382 container = GetRichTextCtrl()->GetFocusObject();
8383
8384 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8385 if (CanPasteFromClipboard())
8386 {
8387 if (wxTheClipboard->Open())
8388 {
8389 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8390 {
8391 wxRichTextBufferDataObject data;
8392 wxTheClipboard->GetData(data);
8393 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8394 if (richTextBuffer)
8395 {
8396 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
8397 if (GetRichTextCtrl())
8398 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
8399 delete richTextBuffer;
8400 }
8401 }
8402 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
8403 #if wxUSE_UNICODE
8404 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8405 #endif
8406 )
8407 {
8408 wxTextDataObject data;
8409 wxTheClipboard->GetData(data);
8410 wxString text(data.GetText());
8411 #ifdef __WXMSW__
8412 wxString text2;
8413 text2.Alloc(text.Length()+1);
8414 size_t i;
8415 for (i = 0; i < text.Length(); i++)
8416 {
8417 wxChar ch = text[i];
8418 if (ch != wxT('\r'))
8419 text2 += ch;
8420 }
8421 #else
8422 wxString text2 = text;
8423 #endif
8424 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
8425
8426 if (GetRichTextCtrl())
8427 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8428
8429 success = true;
8430 }
8431 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8432 {
8433 wxBitmapDataObject data;
8434 wxTheClipboard->GetData(data);
8435 wxBitmap bitmap(data.GetBitmap());
8436 wxImage image(bitmap.ConvertToImage());
8437
8438 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
8439
8440 action->GetNewParagraphs().AddImage(image);
8441
8442 if (action->GetNewParagraphs().GetChildCount() == 1)
8443 action->GetNewParagraphs().SetPartialParagraph(true);
8444
8445 action->SetPosition(position+1);
8446
8447 // Set the range we'll need to delete in Undo
8448 action->SetRange(wxRichTextRange(position+1, position+1));
8449
8450 SubmitAction(action);
8451
8452 success = true;
8453 }
8454 wxTheClipboard->Close();
8455 }
8456 }
8457 #else
8458 wxUnusedVar(position);
8459 #endif
8460 return success;
8461 }
8462
8463 /// Can we paste from the clipboard?
8464 bool wxRichTextBuffer::CanPasteFromClipboard() const
8465 {
8466 bool canPaste = false;
8467 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8468 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8469 {
8470 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8471 #if wxUSE_UNICODE
8472 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8473 #endif
8474 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8475 wxTheClipboard->IsSupported(wxDF_BITMAP))
8476 {
8477 canPaste = true;
8478 }
8479 wxTheClipboard->Close();
8480 }
8481 #endif
8482 return canPaste;
8483 }
8484
8485 /// Dumps contents of buffer for debugging purposes
8486 void wxRichTextBuffer::Dump()
8487 {
8488 wxString text;
8489 {
8490 wxStringOutputStream stream(& text);
8491 wxTextOutputStream textStream(stream);
8492 Dump(textStream);
8493 }
8494
8495 wxLogDebug(text);
8496 }
8497
8498 /// Add an event handler
8499 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8500 {
8501 m_eventHandlers.Append(handler);
8502 return true;
8503 }
8504
8505 /// Remove an event handler
8506 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8507 {
8508 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8509 if (node)
8510 {
8511 m_eventHandlers.Erase(node);
8512 if (deleteHandler)
8513 delete handler;
8514
8515 return true;
8516 }
8517 else
8518 return false;
8519 }
8520
8521 /// Clear event handlers
8522 void wxRichTextBuffer::ClearEventHandlers()
8523 {
8524 m_eventHandlers.Clear();
8525 }
8526
8527 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8528 /// otherwise will stop at the first successful one.
8529 bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8530 {
8531 bool success = false;
8532 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8533 {
8534 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8535 if (handler->ProcessEvent(event))
8536 {
8537 success = true;
8538 if (!sendToAll)
8539 return true;
8540 }
8541 }
8542 return success;
8543 }
8544
8545 /// Set style sheet and notify of the change
8546 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8547 {
8548 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
8549
8550 wxWindowID winid = wxID_ANY;
8551 if (GetRichTextCtrl())
8552 winid = GetRichTextCtrl()->GetId();
8553
8554 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
8555 event.SetEventObject(GetRichTextCtrl());
8556 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
8557 event.SetOldStyleSheet(oldSheet);
8558 event.SetNewStyleSheet(sheet);
8559 event.Allow();
8560
8561 if (SendEvent(event) && !event.IsAllowed())
8562 {
8563 if (sheet != oldSheet)
8564 delete sheet;
8565
8566 return false;
8567 }
8568
8569 if (oldSheet && oldSheet != sheet)
8570 delete oldSheet;
8571
8572 SetStyleSheet(sheet);
8573
8574 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8575 event.SetOldStyleSheet(NULL);
8576 event.Allow();
8577
8578 return SendEvent(event);
8579 }
8580
8581 /// Set renderer, deleting old one
8582 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8583 {
8584 if (sm_renderer)
8585 delete sm_renderer;
8586 sm_renderer = renderer;
8587 }
8588
8589 /// Hit-testing: returns a flag indicating hit test details, plus
8590 /// information about position
8591 int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
8592 {
8593 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
8594 if (ret != wxRICHTEXT_HITTEST_NONE)
8595 {
8596 return ret;
8597 }
8598 else
8599 {
8600 textPosition = m_ownRange.GetEnd()-1;
8601 *obj = this;
8602 *contextObj = this;
8603 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8604 }
8605 }
8606
8607 void wxRichTextBuffer::SetFontScale(double fontScale)
8608 {
8609 m_fontScale = fontScale;
8610 m_fontTable.SetFontScale(fontScale);
8611 }
8612
8613 void wxRichTextBuffer::SetDimensionScale(double dimScale)
8614 {
8615 m_dimensionScale = dimScale;
8616 }
8617
8618 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
8619 {
8620 if (bulletAttr.GetTextColour().IsOk())
8621 {
8622 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8623 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
8624 }
8625 else
8626 {
8627 wxCheckSetPen(dc, *wxBLACK_PEN);
8628 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8629 }
8630
8631 wxFont font;
8632 if (bulletAttr.HasFont())
8633 {
8634 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8635 }
8636 else
8637 font = (*wxNORMAL_FONT);
8638
8639 wxCheckSetFont(dc, font);
8640
8641 int charHeight = dc.GetCharHeight();
8642
8643 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8644 int bulletHeight = bulletWidth;
8645
8646 int x = rect.x;
8647
8648 // Calculate the top position of the character (as opposed to the whole line height)
8649 int y = rect.y + (rect.height - charHeight);
8650
8651 // Calculate where the bullet should be positioned
8652 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
8653
8654 // The margin between a bullet and text.
8655 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8656
8657 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8658 x = rect.x + rect.width - bulletWidth - margin;
8659 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8660 x = x + (rect.width)/2 - bulletWidth/2;
8661
8662 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8663 {
8664 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8665 }
8666 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8667 {
8668 wxPoint pts[5];
8669 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8670 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8671 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8672 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
8673
8674 dc.DrawPolygon(4, pts);
8675 }
8676 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8677 {
8678 wxPoint pts[3];
8679 pts[0].x = x; pts[0].y = y;
8680 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8681 pts[2].x = x; pts[2].y = y + bulletHeight;
8682
8683 dc.DrawPolygon(3, pts);
8684 }
8685 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8686 {
8687 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8688 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8689 }
8690 else // "standard/circle", and catch-all
8691 {
8692 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8693 }
8694
8695 return true;
8696 }
8697
8698 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
8699 {
8700 if (!text.empty())
8701 {
8702 wxFont font;
8703 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8704 {
8705 wxRichTextAttr fontAttr;
8706 if (attr.HasFontPixelSize())
8707 fontAttr.SetFontPixelSize(attr.GetFontSize());
8708 else
8709 fontAttr.SetFontPointSize(attr.GetFontSize());
8710 fontAttr.SetFontStyle(attr.GetFontStyle());
8711 fontAttr.SetFontWeight(attr.GetFontWeight());
8712 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8713 fontAttr.SetFontFaceName(attr.GetBulletFont());
8714 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8715 }
8716 else if (attr.HasFont())
8717 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
8718 else
8719 font = (*wxNORMAL_FONT);
8720
8721 wxCheckSetFont(dc, font);
8722
8723 if (attr.GetTextColour().IsOk())
8724 dc.SetTextForeground(attr.GetTextColour());
8725
8726 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
8727
8728 int charHeight = dc.GetCharHeight();
8729 wxCoord tw, th;
8730 dc.GetTextExtent(text, & tw, & th);
8731
8732 int x = rect.x;
8733
8734 // Calculate the top position of the character (as opposed to the whole line height)
8735 int y = rect.y + (rect.height - charHeight);
8736
8737 // The margin between a bullet and text.
8738 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8739
8740 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8741 x = (rect.x + rect.width) - tw - margin;
8742 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8743 x = x + (rect.width)/2 - tw/2;
8744
8745 dc.DrawText(text, x, y);
8746
8747 return true;
8748 }
8749 else
8750 return false;
8751 }
8752
8753 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
8754 {
8755 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8756 // with the buffer. The store will allow retrieval from memory, disk or other means.
8757 return false;
8758 }
8759
8760 /// Enumerate the standard bullet names currently supported
8761 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8762 {
8763 bulletNames.Add(wxTRANSLATE("standard/circle"));
8764 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
8765 bulletNames.Add(wxTRANSLATE("standard/square"));
8766 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8767 bulletNames.Add(wxTRANSLATE("standard/triangle"));
8768
8769 return true;
8770 }
8771
8772 /*!
8773 * wxRichTextBox
8774 */
8775
8776 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
8777
8778 wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
8779 wxRichTextParagraphLayoutBox(parent)
8780 {
8781 }
8782
8783 /// Draw the item
8784 bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8785 {
8786 if (!IsShown())
8787 return true;
8788
8789 // TODO: if the active object in the control, draw an indication.
8790 // We need to add the concept of active object, and not just focus object,
8791 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8792 // Ultimately we would like to be able to interactively resize an active object
8793 // using drag handles.
8794 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8795 }
8796
8797 /// Copy
8798 void wxRichTextBox::Copy(const wxRichTextBox& obj)
8799 {
8800 wxRichTextParagraphLayoutBox::Copy(obj);
8801 }
8802
8803 // Edit properties via a GUI
8804 bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8805 {
8806 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8807 boxDlg.SetAttributes(GetAttributes());
8808
8809 if (boxDlg.ShowModal() == wxID_OK)
8810 {
8811 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8812 // indeterminate in the object.
8813 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8814 return true;
8815 }
8816 else
8817 return false;
8818 }
8819
8820 /*!
8821 * wxRichTextField
8822 */
8823
8824 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8825
8826 wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8827 wxRichTextParagraphLayoutBox(parent)
8828 {
8829 SetFieldType(fieldType);
8830 }
8831
8832 /// Draw the item
8833 bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8834 {
8835 if (!IsShown())
8836 return true;
8837
8838 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8839 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8840 return true;
8841
8842 // Fallback; but don't draw guidelines.
8843 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8844 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8845 }
8846
8847 bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8848 {
8849 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8850 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8851 return true;
8852
8853 // Fallback
8854 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8855 }
8856
8857 bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
8858 {
8859 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8860 if (fieldType)
8861 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents);
8862
8863 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
8864 }
8865
8866 /// Calculate range
8867 void wxRichTextField::CalculateRange(long start, long& end)
8868 {
8869 if (IsTopLevel())
8870 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8871 else
8872 wxRichTextObject::CalculateRange(start, end);
8873 }
8874
8875 /// Copy
8876 void wxRichTextField::Copy(const wxRichTextField& obj)
8877 {
8878 wxRichTextParagraphLayoutBox::Copy(obj);
8879
8880 UpdateField(GetBuffer());
8881 }
8882
8883 // Edit properties via a GUI
8884 bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8885 {
8886 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8887 if (fieldType)
8888 return fieldType->EditProperties(this, parent, buffer);
8889
8890 return false;
8891 }
8892
8893 bool wxRichTextField::CanEditProperties() const
8894 {
8895 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8896 if (fieldType)
8897 return fieldType->CanEditProperties((wxRichTextField*) this);
8898
8899 return false;
8900 }
8901
8902 wxString wxRichTextField::GetPropertiesMenuLabel() const
8903 {
8904 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8905 if (fieldType)
8906 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8907
8908 return wxEmptyString;
8909 }
8910
8911 bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
8912 {
8913 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8914 if (fieldType)
8915 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
8916
8917 return false;
8918 }
8919
8920 bool wxRichTextField::IsTopLevel() const
8921 {
8922 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8923 if (fieldType)
8924 return fieldType->IsTopLevel((wxRichTextField*) this);
8925
8926 return true;
8927 }
8928
8929 IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8930
8931 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8932
8933 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8934 {
8935 Init();
8936
8937 SetName(name);
8938 SetLabel(label);
8939 SetDisplayStyle(displayStyle);
8940 }
8941
8942 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8943 {
8944 Init();
8945
8946 SetName(name);
8947 SetBitmap(bitmap);
8948 SetDisplayStyle(displayStyle);
8949 }
8950
8951 void wxRichTextFieldTypeStandard::Init()
8952 {
8953 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8954 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8955 m_textColour = *wxWHITE;
8956 m_borderColour = *wxBLACK;
8957 m_backgroundColour = *wxBLACK;
8958 m_verticalPadding = 1;
8959 m_horizontalPadding = 3;
8960 m_horizontalMargin = 2;
8961 m_verticalMargin = 0;
8962 }
8963
8964 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
8965 {
8966 wxRichTextFieldType::Copy(field);
8967
8968 m_label = field.m_label;
8969 m_displayStyle = field.m_displayStyle;
8970 m_font = field.m_font;
8971 m_textColour = field.m_textColour;
8972 m_borderColour = field.m_borderColour;
8973 m_backgroundColour = field.m_backgroundColour;
8974 m_verticalPadding = field.m_verticalPadding;
8975 m_horizontalPadding = field.m_horizontalPadding;
8976 m_horizontalMargin = field.m_horizontalMargin;
8977 m_bitmap = field.m_bitmap;
8978 }
8979
8980 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))
8981 {
8982 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
8983 return false; // USe default composite drawing
8984 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
8985 {
8986 int borderSize = 1;
8987
8988 wxPen borderPen(m_borderColour, 1, wxSOLID);
8989 wxBrush backgroundBrush(m_backgroundColour);
8990 wxColour textColour(m_textColour);
8991
8992 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
8993 {
8994 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
8995 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
8996
8997 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
8998 backgroundBrush = wxBrush(highlightColour);
8999
9000 wxCheckSetBrush(dc, backgroundBrush);
9001 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9002 dc.DrawRectangle(rect);
9003 }
9004
9005 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9006 borderSize = 0;
9007
9008 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9009 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9010 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9011
9012 // clientArea is where the text is actually written
9013 wxRect clientArea = objectRect;
9014
9015 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9016 {
9017 dc.SetPen(borderPen);
9018 dc.SetBrush(backgroundBrush);
9019 dc.DrawRoundedRectangle(objectRect, 4.0);
9020 }
9021 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9022 {
9023 int arrowLength = objectRect.height/2;
9024 clientArea.width -= (arrowLength - m_horizontalPadding);
9025
9026 wxPoint pts[5];
9027 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9028 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9029 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9030 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9031 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9032 dc.SetPen(borderPen);
9033 dc.SetBrush(backgroundBrush);
9034 dc.DrawPolygon(5, pts);
9035 }
9036 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9037 {
9038 int arrowLength = objectRect.height/2;
9039 clientArea.width -= (arrowLength - m_horizontalPadding);
9040 clientArea.x += (arrowLength - m_horizontalPadding);
9041
9042 wxPoint pts[5];
9043 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9044 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9045 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9046 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9047 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9048 dc.SetPen(borderPen);
9049 dc.SetBrush(backgroundBrush);
9050 dc.DrawPolygon(5, pts);
9051 }
9052
9053 if (m_bitmap.IsOk())
9054 {
9055 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9056 int y = clientArea.y + m_verticalPadding;
9057 dc.DrawBitmap(m_bitmap, x, y, true);
9058
9059 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9060 {
9061 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9062 wxCheckSetPen(dc, *wxBLACK_PEN);
9063 dc.SetLogicalFunction(wxINVERT);
9064 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9065 dc.SetLogicalFunction(wxCOPY);
9066 }
9067 }
9068 else
9069 {
9070 wxString label(m_label);
9071 if (label.IsEmpty())
9072 label = wxT("??");
9073 int w, h, maxDescent;
9074 dc.SetFont(m_font);
9075 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9076 dc.SetTextForeground(textColour);
9077
9078 int x = clientArea.x + (clientArea.width - w)/2;
9079 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9080 dc.DrawText(m_label, x, y);
9081 }
9082 }
9083
9084 return true;
9085 }
9086
9087 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9088 {
9089 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9090 return false; // USe default composite layout
9091
9092 wxSize size = GetSize(obj, dc, context, style);
9093 obj->SetCachedSize(size);
9094 obj->SetMinSize(size);
9095 obj->SetMaxSize(size);
9096 return true;
9097 }
9098
9099 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
9100 {
9101 if (IsTopLevel(obj))
9102 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position);
9103 else
9104 {
9105 wxSize sz = GetSize(obj, dc, context, 0);
9106 if (partialExtents)
9107 {
9108 int lastSize;
9109 if (partialExtents->GetCount() > 0)
9110 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9111 else
9112 lastSize = 0;
9113 partialExtents->Add(lastSize + sz.x);
9114 }
9115 size = sz;
9116 return true;
9117 }
9118 }
9119
9120 wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9121 {
9122 int borderSize = 1;
9123 int w = 0, h = 0, maxDescent = 0;
9124
9125 wxSize sz;
9126 if (m_bitmap.IsOk())
9127 {
9128 w = m_bitmap.GetWidth();
9129 h = m_bitmap.GetHeight();
9130
9131 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9132 }
9133 else
9134 {
9135 wxString label(m_label);
9136 if (label.IsEmpty())
9137 label = wxT("??");
9138 dc.SetFont(m_font);
9139 dc.GetTextExtent(label, & w, &h, & maxDescent);
9140
9141 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9142 }
9143
9144 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9145 {
9146 sz.x += borderSize*2;
9147 sz.y += borderSize*2;
9148 }
9149
9150 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9151 {
9152 // Add space for the arrow
9153 sz.x += (sz.y/2 - m_horizontalPadding);
9154 }
9155
9156 return sz;
9157 }
9158
9159 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9160
9161 wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9162 wxRichTextBox(parent)
9163 {
9164 }
9165
9166 /// Draw the item
9167 bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9168 {
9169 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9170 }
9171
9172 /// Copy
9173 void wxRichTextCell::Copy(const wxRichTextCell& obj)
9174 {
9175 wxRichTextBox::Copy(obj);
9176 }
9177
9178 // Edit properties via a GUI
9179 bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9180 {
9181 // We need to gather common attributes for all selected cells.
9182
9183 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9184 bool multipleCells = false;
9185 wxRichTextAttr attr;
9186
9187 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9188 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9189 {
9190 wxRichTextAttr clashingAttr, absentAttr;
9191 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9192 size_t i;
9193 int selectedCellCount = 0;
9194 for (i = 0; i < sel.GetCount(); i++)
9195 {
9196 const wxRichTextRange& range = sel[i];
9197 wxRichTextCell* cell = table->GetCell(range.GetStart());
9198 if (cell)
9199 {
9200 wxRichTextAttr cellStyle = cell->GetAttributes();
9201
9202 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9203
9204 selectedCellCount ++;
9205 }
9206 }
9207 multipleCells = selectedCellCount > 1;
9208 }
9209 else
9210 {
9211 attr = GetAttributes();
9212 }
9213
9214 wxString caption;
9215 if (multipleCells)
9216 caption = _("Multiple Cell Properties");
9217 else
9218 caption = _("Cell Properties");
9219
9220 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9221 cellDlg.SetAttributes(attr);
9222
9223 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
9224 if (sizePage)
9225 {
9226 // We don't want position and floating controls for a cell.
9227 sizePage->ShowPositionControls(false);
9228 sizePage->ShowFloatingControls(false);
9229 }
9230
9231 if (cellDlg.ShowModal() == wxID_OK)
9232 {
9233 if (multipleCells)
9234 {
9235 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9236 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9237 // since it may represent clashing attributes across multiple objects.
9238 table->SetCellStyle(sel, attr);
9239 }
9240 else
9241 // For a single object, indeterminate attributes set by the user should be reflected in the
9242 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9243 // the style directly instead of applying (which ignores indeterminate attributes,
9244 // leaving them as they were).
9245 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9246 return true;
9247 }
9248 else
9249 return false;
9250 }
9251
9252 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9253
9254 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9255
9256 wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9257 {
9258 m_rowCount = 0;
9259 m_colCount = 0;
9260 }
9261
9262 // Draws the object.
9263 bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9264 {
9265 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9266 }
9267
9268 WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9269 WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9270
9271 // Lays the object out. rect is the space available for layout. Often it will
9272 // be the specified overall space for this object, if trying to constrain
9273 // layout to a particular size, or it could be the total space available in the
9274 // parent. rect is the overall size, so we must subtract margins and padding.
9275 // to get the actual available space.
9276 bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
9277 {
9278 SetPosition(rect.GetPosition());
9279
9280 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9281 // minimum size if within alloted size, then divide up remaining size
9282 // between rows/cols.
9283
9284 double scale = 1.0;
9285 wxRichTextBuffer* buffer = GetBuffer();
9286 if (buffer) scale = buffer->GetScale();
9287
9288 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
9289 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9290
9291 wxRichTextAttr attr(GetAttributes());
9292 context.ApplyVirtualAttributes(attr, this);
9293
9294 // If we have no fixed table size, and assuming we're not pushed for
9295 // space, then we don't have to try to stretch the table to fit the contents.
9296 bool stretchToFitTableWidth = false;
9297
9298 int tableWidth = rect.width;
9299 if (attr.GetTextBoxAttr().GetWidth().IsValid())
9300 {
9301 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
9302
9303 // Fixed table width, so we do want to stretch columns out if necessary.
9304 stretchToFitTableWidth = true;
9305
9306 // Shouldn't be able to exceed the size passed to this function
9307 tableWidth = wxMin(rect.width, tableWidth);
9308 }
9309
9310 // Get internal padding
9311 int paddingLeft = 0, paddingTop = 0;
9312 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9313 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9314 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9315 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
9316
9317 // Assume that left and top padding are also used for inter-cell padding.
9318 int paddingX = paddingLeft;
9319 int paddingY = paddingTop;
9320
9321 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
9322 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
9323
9324 // Internal table width - the area for content
9325 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9326
9327 int rowCount = m_cells.GetCount();
9328 if (m_colCount == 0 || rowCount == 0)
9329 {
9330 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9331 SetCachedSize(overallRect.GetSize());
9332
9333 // Zero content size
9334 SetMinSize(overallRect.GetSize());
9335 SetMaxSize(GetMinSize());
9336 return true;
9337 }
9338
9339 // The final calculated widths
9340 wxArrayInt colWidths;
9341 colWidths.Add(0, m_colCount);
9342
9343 wxArrayInt absoluteColWidths;
9344 absoluteColWidths.Add(0, m_colCount);
9345
9346 wxArrayInt percentageColWidths;
9347 percentageColWidths.Add(0, m_colCount);
9348 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9349 // These are only relevant when the first column contains spanning information.
9350 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9351 wxArrayInt maxColWidths;
9352 maxColWidths.Add(0, m_colCount);
9353 wxArrayInt minColWidths;
9354 minColWidths.Add(0, m_colCount);
9355
9356 wxSize tableSize(tableWidth, 0);
9357
9358 int i, j, k;
9359
9360 for (i = 0; i < m_colCount; i++)
9361 {
9362 absoluteColWidths[i] = 0;
9363 // absoluteColWidthsSpanning[i] = 0;
9364 percentageColWidths[i] = -1;
9365 // percentageColWidthsSpanning[i] = -1;
9366 colWidths[i] = 0;
9367 maxColWidths[i] = 0;
9368 minColWidths[i] = 0;
9369 // columnSpans[i] = 1;
9370 }
9371
9372 // (0) Determine which cells are visible according to spans
9373 // 1 2 3 4 5
9374 // __________________
9375 // | | | | | 1
9376 // |------| |----|
9377 // |------| | | 2
9378 // |------| | | 3
9379 // |------------------|
9380 // |__________________| 4
9381
9382 // To calculate cell visibility:
9383 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9384 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9385 // that cell, hide the cell.
9386
9387 // We can also use this array to match the size of spanning cells to the grid. Or just do
9388 // this when we iterate through all cells.
9389
9390 // 0.1: add spanning cells to an array
9391 wxRichTextRectArray rectArray;
9392 for (j = 0; j < m_rowCount; j++)
9393 {
9394 for (i = 0; i < m_colCount; i++)
9395 {
9396 wxRichTextBox* cell = GetCell(j, i);
9397 int colSpan = 1, rowSpan = 1;
9398 if (cell->GetProperties().HasProperty(wxT("colspan")))
9399 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9400 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9401 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9402 if (colSpan > 1 || rowSpan > 1)
9403 {
9404 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9405 }
9406 }
9407 }
9408 // 0.2: find which cells are subsumed by a spanning cell
9409 for (j = 0; j < m_rowCount; j++)
9410 {
9411 for (i = 0; i < m_colCount; i++)
9412 {
9413 wxRichTextBox* cell = GetCell(j, i);
9414 if (rectArray.GetCount() == 0)
9415 {
9416 cell->Show(true);
9417 }
9418 else
9419 {
9420 int colSpan = 1, rowSpan = 1;
9421 if (cell->GetProperties().HasProperty(wxT("colspan")))
9422 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9423 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9424 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9425 if (colSpan > 1 || rowSpan > 1)
9426 {
9427 // Assume all spanning cells are shown
9428 cell->Show(true);
9429 }
9430 else
9431 {
9432 bool shown = true;
9433 for (k = 0; k < (int) rectArray.GetCount(); k++)
9434 {
9435 if (rectArray[k].Contains(wxPoint(i, j)))
9436 {
9437 shown = false;
9438 break;
9439 }
9440 }
9441 cell->Show(shown);
9442 }
9443 }
9444 }
9445 }
9446
9447 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9448 // overlap with a spanned cell starting at a previous column position.
9449 // This means we need to keep an array of rects so we can check. However
9450 // it does also mean that some spans simply may not be taken into account
9451 // where there are different spans happening on different rows. In these cases,
9452 // they will simply be as wide as their constituent columns.
9453
9454 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9455 // the absolute or percentage width of each column.
9456
9457 for (j = 0; j < m_rowCount; j++)
9458 {
9459 // First get the overall margins so we can calculate percentage widths based on
9460 // the available content space for all cells on the row
9461
9462 int overallRowContentMargin = 0;
9463 int visibleCellCount = 0;
9464
9465 for (i = 0; i < m_colCount; i++)
9466 {
9467 wxRichTextBox* cell = GetCell(j, i);
9468 if (cell->IsShown())
9469 {
9470 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9471 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9472
9473 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9474 visibleCellCount ++;
9475 }
9476 }
9477
9478 // Add in inter-cell padding
9479 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9480
9481 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9482 wxSize rowTableSize(rowContentWidth, 0);
9483 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9484
9485 for (i = 0; i < m_colCount; i++)
9486 {
9487 wxRichTextBox* cell = GetCell(j, i);
9488 if (cell->IsShown())
9489 {
9490 int colSpan = 1;
9491 if (cell->GetProperties().HasProperty(wxT("colspan")))
9492 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9493
9494 // Lay out cell to find min/max widths
9495 cell->Invalidate(wxRICHTEXT_ALL);
9496 cell->Layout(dc, context, availableSpace, availableSpace, style);
9497
9498 if (colSpan == 1)
9499 {
9500 int absoluteCellWidth = -1;
9501 int percentageCellWidth = -1;
9502
9503 // I think we need to calculate percentages from the internal table size,
9504 // minus the padding between cells which we'll need to calculate from the
9505 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9506 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9507 // so if we want to conform to that we'll need to add in the overall cell margins.
9508 // However, this will make it difficult to specify percentages that add up to
9509 // 100% and still fit within the table width.
9510 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9511 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9512 // If we're using internal content size for the width, we would calculate the
9513 // the overall cell width for n cells as:
9514 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9515 // + thisOverallCellMargin
9516 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9517 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9518
9519 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9520 {
9521 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9522 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9523 {
9524 percentageCellWidth = w;
9525 }
9526 else
9527 {
9528 absoluteCellWidth = w;
9529 }
9530 // Override absolute width with minimum width if necessary
9531 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9532 absoluteCellWidth = cell->GetMinSize().x;
9533 }
9534
9535 if (absoluteCellWidth != -1)
9536 {
9537 if (absoluteCellWidth > absoluteColWidths[i])
9538 absoluteColWidths[i] = absoluteCellWidth;
9539 }
9540
9541 if (percentageCellWidth != -1)
9542 {
9543 if (percentageCellWidth > percentageColWidths[i])
9544 percentageColWidths[i] = percentageCellWidth;
9545 }
9546
9547 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9548 minColWidths[i] = cell->GetMinSize().x;
9549 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9550 maxColWidths[i] = cell->GetMaxSize().x;
9551 }
9552 }
9553 }
9554 }
9555
9556 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9557 // TODO: simply merge this into (1).
9558 for (i = 0; i < m_colCount; i++)
9559 {
9560 if (absoluteColWidths[i] > 0)
9561 {
9562 colWidths[i] = absoluteColWidths[i];
9563 }
9564 else if (percentageColWidths[i] > 0)
9565 {
9566 colWidths[i] = percentageColWidths[i];
9567
9568 // This is rubbish - we calculated the absolute widths from percentages, so
9569 // we can't do it again here.
9570 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9571 }
9572 }
9573
9574 // (3) Process absolute or proportional widths of spanning columns,
9575 // now that we know what our fixed column widths are going to be.
9576 // Spanned cells will try to adjust columns so the span will fit.
9577 // Even existing fixed column widths can be expanded if necessary.
9578 // Actually, currently fixed columns widths aren't adjusted; instead,
9579 // the algorithm favours earlier rows and adjusts unspecified column widths
9580 // the first time only. After that, we can't know whether the column has been
9581 // specified explicitly or not. (We could make a note if necessary.)
9582 for (j = 0; j < m_rowCount; j++)
9583 {
9584 // First get the overall margins so we can calculate percentage widths based on
9585 // the available content space for all cells on the row
9586
9587 int overallRowContentMargin = 0;
9588 int visibleCellCount = 0;
9589
9590 for (i = 0; i < m_colCount; i++)
9591 {
9592 wxRichTextBox* cell = GetCell(j, i);
9593 if (cell->IsShown())
9594 {
9595 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9596 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9597
9598 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9599 visibleCellCount ++;
9600 }
9601 }
9602
9603 // Add in inter-cell padding
9604 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9605
9606 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9607 wxSize rowTableSize(rowContentWidth, 0);
9608 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9609
9610 for (i = 0; i < m_colCount; i++)
9611 {
9612 wxRichTextBox* cell = GetCell(j, i);
9613 if (cell->IsShown())
9614 {
9615 int colSpan = 1;
9616 if (cell->GetProperties().HasProperty(wxT("colspan")))
9617 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9618
9619 if (colSpan > 1)
9620 {
9621 int spans = wxMin(colSpan, m_colCount - i);
9622 int cellWidth = 0;
9623 if (spans > 0)
9624 {
9625 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9626 {
9627 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9628 // Override absolute width with minimum width if necessary
9629 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9630 cellWidth = cell->GetMinSize().x;
9631 }
9632 else
9633 {
9634 // Do we want to do this? It's the only chance we get to
9635 // use the cell's min/max sizes, so we need to work out
9636 // how we're going to balance the unspecified spanning cell
9637 // width with the possibility more-constrained constituent cell widths.
9638 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9639 // don't want to constraint all the spanned columns to fit into this cell.
9640 // OK, let's say that if any of the constituent columns don't fit,
9641 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9642 // cells to the columns later.
9643 cellWidth = cell->GetMinSize().x;
9644 if (cell->GetMaxSize().x > cellWidth)
9645 cellWidth = cell->GetMaxSize().x;
9646 }
9647
9648 // Subtract the padding between cells
9649 int spanningWidth = cellWidth;
9650 spanningWidth -= paddingX * (spans-1);
9651
9652 if (spanningWidth > 0)
9653 {
9654 // Now share the spanning width between columns within that span
9655 // TODO: take into account min widths of columns within the span
9656 int spanningWidthLeft = spanningWidth;
9657 int stretchColCount = 0;
9658 for (k = i; k < (i+spans); k++)
9659 {
9660 if (colWidths[k] > 0) // absolute or proportional width has been specified
9661 spanningWidthLeft -= colWidths[k];
9662 else
9663 stretchColCount ++;
9664 }
9665 // Now divide what's left between the remaining columns
9666 int colShare = 0;
9667 if (stretchColCount > 0)
9668 colShare = spanningWidthLeft / stretchColCount;
9669 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9670
9671 // If fixed-width columns are currently too big, then we'll later
9672 // stretch the spanned cell to fit.
9673
9674 if (spanningWidthLeft > 0)
9675 {
9676 for (k = i; k < (i+spans); k++)
9677 {
9678 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9679 {
9680 int newWidth = colShare;
9681 if (k == (i+spans-1))
9682 newWidth += colShareRemainder; // ensure all pixels are filled
9683 colWidths[k] = newWidth;
9684 }
9685 }
9686 }
9687 }
9688 }
9689 }
9690 }
9691 }
9692 }
9693
9694 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9695 // TODO: take into account min widths of columns within the span
9696 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9697 int widthLeft = tableWidthMinusPadding;
9698 int stretchColCount = 0;
9699 for (i = 0; i < m_colCount; i++)
9700 {
9701 // TODO: we need to take into account min widths.
9702 // Subtract min width from width left, then
9703 // add the colShare to the min width
9704 if (colWidths[i] > 0) // absolute or proportional width has been specified
9705 widthLeft -= colWidths[i];
9706 else
9707 {
9708 if (minColWidths[i] > 0)
9709 widthLeft -= minColWidths[i];
9710
9711 stretchColCount ++;
9712 }
9713 }
9714
9715 // Now divide what's left between the remaining columns
9716 int colShare = 0;
9717 if (stretchColCount > 0)
9718 colShare = widthLeft / stretchColCount;
9719 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9720
9721 // Check we don't have enough space, in which case shrink all columns, overriding
9722 // any absolute/proportional widths
9723 // TODO: actually we would like to divide up the shrinkage according to size.
9724 // How do we calculate the proportions that will achieve this?
9725 // Could first choose an arbitrary value for stretching cells, and then calculate
9726 // factors to multiply each width by.
9727 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9728 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9729 {
9730 colShare = tableWidthMinusPadding / m_colCount;
9731 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9732 for (i = 0; i < m_colCount; i++)
9733 {
9734 colWidths[i] = 0;
9735 minColWidths[i] = 0;
9736 }
9737 }
9738
9739 // We have to adjust the columns if either we need to shrink the
9740 // table to fit the parent/table width, or we explicitly set the
9741 // table width and need to stretch out the table.
9742 if (widthLeft < 0 || stretchToFitTableWidth)
9743 {
9744 for (i = 0; i < m_colCount; i++)
9745 {
9746 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9747 {
9748 if (minColWidths[i] > 0)
9749 colWidths[i] = minColWidths[i] + colShare;
9750 else
9751 colWidths[i] = colShare;
9752 if (i == (m_colCount-1))
9753 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9754 }
9755 }
9756 }
9757
9758 // TODO: if spanned cells have no specified or max width, make them the
9759 // as big as the columns they span. Do this for all spanned cells in all
9760 // rows, of course. Size any spanned cells left over at the end - even if they
9761 // have width > 0, make sure they're limited to the appropriate column edge.
9762
9763
9764 /*
9765 Sort out confusion between content width
9766 and overall width later. For now, assume we specify overall width.
9767
9768 So, now we've laid out the table to fit into the given space
9769 and have used specified widths and minimum widths.
9770
9771 Now we need to consider how we will try to take maximum width into account.
9772
9773 */
9774
9775 // (??) TODO: take max width into account
9776
9777 // (6) Lay out all cells again with the current values
9778
9779 int maxRight = 0;
9780 int y = availableSpace.y;
9781 for (j = 0; j < m_rowCount; j++)
9782 {
9783 int x = availableSpace.x; // TODO: take into account centering etc.
9784 int maxCellHeight = 0;
9785 int maxSpecifiedCellHeight = 0;
9786
9787 wxArrayInt actualWidths;
9788 actualWidths.Add(0, m_colCount);
9789
9790 wxTextAttrDimensionConverter converter(dc, scale);
9791 for (i = 0; i < m_colCount; i++)
9792 {
9793 wxRichTextCell* cell = GetCell(j, i);
9794 if (cell->IsShown())
9795 {
9796 // Get max specified cell height
9797 // Don't handle percentages for height
9798 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9799 {
9800 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9801 if (h > maxSpecifiedCellHeight)
9802 maxSpecifiedCellHeight = h;
9803 }
9804
9805 if (colWidths[i] > 0) // absolute or proportional width has been specified
9806 {
9807 int colSpan = 1;
9808 if (cell->GetProperties().HasProperty(wxT("colspan")))
9809 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9810
9811 wxRect availableCellSpace;
9812
9813 // TODO: take into acount spans
9814 if (colSpan > 1)
9815 {
9816 // Calculate the size of this spanning cell from its constituent columns
9817 int xx = x;
9818 int spans = wxMin(colSpan, m_colCount - i);
9819 for (k = i; k < spans; k++)
9820 {
9821 if (k != i)
9822 xx += paddingX;
9823 xx += colWidths[k];
9824 }
9825 availableCellSpace = wxRect(x, y, xx, -1);
9826 }
9827 else
9828 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9829
9830 // Store actual width so we can force cell to be the appropriate width on the final loop
9831 actualWidths[i] = availableCellSpace.GetWidth();
9832
9833 // Lay out cell
9834 cell->Invalidate(wxRICHTEXT_ALL);
9835 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9836
9837 // TODO: use GetCachedSize().x to compute 'natural' size
9838
9839 x += (availableCellSpace.GetWidth() + paddingX);
9840 if (cell->GetCachedSize().y > maxCellHeight)
9841 maxCellHeight = cell->GetCachedSize().y;
9842 }
9843 }
9844 }
9845
9846 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9847
9848 for (i = 0; i < m_colCount; i++)
9849 {
9850 wxRichTextCell* cell = GetCell(j, i);
9851 if (cell->IsShown())
9852 {
9853 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9854 // Lay out cell with new height
9855 cell->Invalidate(wxRICHTEXT_ALL);
9856 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9857
9858 // Make sure the cell size really is the appropriate size,
9859 // not the calculated box size
9860 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9861
9862 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9863 }
9864 }
9865
9866 y += maxCellHeight;
9867 if (j < (m_rowCount-1))
9868 y += paddingY;
9869 }
9870
9871 // We need to add back the margins etc.
9872 {
9873 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9874 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
9875 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
9876 SetCachedSize(marginRect.GetSize());
9877 }
9878
9879 // TODO: calculate max size
9880 {
9881 SetMaxSize(GetCachedSize());
9882 }
9883
9884 // TODO: calculate min size
9885 {
9886 SetMinSize(GetCachedSize());
9887 }
9888
9889 // TODO: currently we use either a fixed table width or the parent's size.
9890 // We also want to be able to calculate the table width from its content,
9891 // whether using fixed column widths or cell content min/max width.
9892 // Probably need a boolean flag to say whether we need to stretch cells
9893 // to fit the table width, or to simply use min/max cell widths. The
9894 // trouble with this is that if cell widths are not specified, they
9895 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9896 // Anyway, ignoring that problem, we probably need to factor layout into a function
9897 // that can can calculate the maximum unconstrained layout in case table size is
9898 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9899 // constrain Layout(), or the previously-calculated max size to constraint layout.
9900
9901 return true;
9902 }
9903
9904 // Finds the absolute position and row height for the given character position
9905 bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
9906 {
9907 wxRichTextCell* child = GetCell(index+1);
9908 if (child)
9909 {
9910 // Find the position at the start of the child cell, since the table doesn't
9911 // have any caret position of its own.
9912 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
9913 }
9914 else
9915 return false;
9916 }
9917
9918 // Get the cell at the given character position (in the range of the table).
9919 wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9920 {
9921 int row = 0, col = 0;
9922 if (GetCellRowColumnPosition(pos, row, col))
9923 {
9924 return GetCell(row, col);
9925 }
9926 else
9927 return NULL;
9928 }
9929
9930 // Get the row/column for a given character position
9931 bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9932 {
9933 if (m_colCount == 0 || m_rowCount == 0)
9934 return false;
9935
9936 row = (int) (pos / m_colCount);
9937 col = pos - (row * m_colCount);
9938
9939 wxASSERT(row < m_rowCount && col < m_colCount);
9940
9941 if (row < m_rowCount && col < m_colCount)
9942 return true;
9943 else
9944 return false;
9945 }
9946
9947 // Calculate range, taking row/cell ordering into account instead of relying
9948 // on list ordering.
9949 void wxRichTextTable::CalculateRange(long start, long& end)
9950 {
9951 long current = start;
9952 long lastEnd = current;
9953
9954 if (IsTopLevel())
9955 {
9956 current = 0;
9957 lastEnd = 0;
9958 }
9959
9960 int i, j;
9961 for (i = 0; i < m_rowCount; i++)
9962 {
9963 for (j = 0; j < m_colCount; j++)
9964 {
9965 wxRichTextCell* child = GetCell(i, j);
9966 if (child)
9967 {
9968 long childEnd = 0;
9969
9970 child->CalculateRange(current, childEnd);
9971
9972 lastEnd = childEnd;
9973 current = childEnd + 1;
9974 }
9975 }
9976 }
9977
9978 // A top-level object always has a range of size 1,
9979 // because its children don't count at this level.
9980 end = start;
9981 m_range.SetRange(start, start);
9982
9983 // An object with no children has zero length
9984 if (m_children.GetCount() == 0)
9985 lastEnd --;
9986 m_ownRange.SetRange(0, lastEnd);
9987 }
9988
9989 // Gets the range size.
9990 bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
9991 {
9992 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
9993 }
9994
9995 // Deletes content in the given range.
9996 bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
9997 {
9998 // TODO: implement deletion of cells
9999 return true;
10000 }
10001
10002 // Gets any text in this object for the given range.
10003 wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10004 {
10005 return wxRichTextBox::GetTextForRange(range);
10006 }
10007
10008 // Copies this object.
10009 void wxRichTextTable::Copy(const wxRichTextTable& obj)
10010 {
10011 wxRichTextBox::Copy(obj);
10012
10013 ClearTable();
10014
10015 m_rowCount = obj.m_rowCount;
10016 m_colCount = obj.m_colCount;
10017
10018 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10019
10020 int i, j;
10021 for (i = 0; i < m_rowCount; i++)
10022 {
10023 wxRichTextObjectPtrArray& colArray = m_cells[i];
10024 for (j = 0; j < m_colCount; j++)
10025 {
10026 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10027 AppendChild(cell);
10028
10029 colArray.Add(cell);
10030 }
10031 }
10032 }
10033
10034 void wxRichTextTable::ClearTable()
10035 {
10036 m_cells.Clear();
10037 DeleteChildren();
10038 }
10039
10040 bool wxRichTextTable::CreateTable(int rows, int cols)
10041 {
10042 ClearTable();
10043
10044 m_rowCount = rows;
10045 m_colCount = cols;
10046
10047 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10048
10049 int i, j;
10050 for (i = 0; i < rows; i++)
10051 {
10052 wxRichTextObjectPtrArray& colArray = m_cells[i];
10053 for (j = 0; j < cols; j++)
10054 {
10055 wxRichTextCell* cell = new wxRichTextCell;
10056 AppendChild(cell);
10057 cell->AddParagraph(wxEmptyString);
10058
10059 colArray.Add(cell);
10060 }
10061 }
10062
10063 return true;
10064 }
10065
10066 wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10067 {
10068 wxASSERT(row < m_rowCount);
10069 wxASSERT(col < m_colCount);
10070
10071 if (row < m_rowCount && col < m_colCount)
10072 {
10073 wxRichTextObjectPtrArray& colArray = m_cells[row];
10074 wxRichTextObject* obj = colArray[col];
10075 return wxDynamicCast(obj, wxRichTextCell);
10076 }
10077 else
10078 return NULL;
10079 }
10080
10081 // Returns a selection object specifying the selections between start and end character positions.
10082 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10083 wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10084 {
10085 wxRichTextSelection selection;
10086 selection.SetContainer((wxRichTextTable*) this);
10087
10088 if (start > end)
10089 {
10090 long tmp = end;
10091 end = start;
10092 start = tmp;
10093 }
10094
10095 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10096
10097 if (end >= (m_colCount * m_rowCount))
10098 return selection;
10099
10100 // We need to find the rectangle of cells that is described by the rectangle
10101 // with start, end as the diagonal. Make sure we don't add cells that are
10102 // not currenty visible because they are overlapped by spanning cells.
10103 /*
10104 --------------------------
10105 | 0 | 1 | 2 | 3 | 4 |
10106 --------------------------
10107 | 5 | 6 | 7 | 8 | 9 |
10108 --------------------------
10109 | 10 | 11 | 12 | 13 | 14 |
10110 --------------------------
10111 | 15 | 16 | 17 | 18 | 19 |
10112 --------------------------
10113
10114 Let's say we select 6 -> 18.
10115
10116 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10117 which is left and which is right.
10118
10119 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10120
10121 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10122 and (b) shown.
10123
10124
10125 */
10126
10127 int leftCol = start - m_colCount * int(start/m_colCount);
10128 int rightCol = end - m_colCount * int(end/m_colCount);
10129
10130 int topRow = int(start/m_colCount);
10131 int bottomRow = int(end/m_colCount);
10132
10133 if (leftCol > rightCol)
10134 {
10135 int tmp = rightCol;
10136 rightCol = leftCol;
10137 leftCol = tmp;
10138 }
10139
10140 if (topRow > bottomRow)
10141 {
10142 int tmp = bottomRow;
10143 bottomRow = topRow;
10144 topRow = tmp;
10145 }
10146
10147 int i, j;
10148 for (i = topRow; i <= bottomRow; i++)
10149 {
10150 for (j = leftCol; j <= rightCol; j++)
10151 {
10152 wxRichTextCell* cell = GetCell(i, j);
10153 if (cell && cell->IsShown())
10154 selection.Add(cell->GetRange());
10155 }
10156 }
10157
10158 return selection;
10159 }
10160
10161 // Sets the attributes for the cells specified by the selection.
10162 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10163 {
10164 if (selection.GetContainer() != this)
10165 return false;
10166
10167 wxRichTextBuffer* buffer = GetBuffer();
10168 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10169 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10170
10171 if (withUndo)
10172 buffer->BeginBatchUndo(_("Set Cell Style"));
10173
10174 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10175 while (node)
10176 {
10177 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10178 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10179 SetStyle(cell, style, flags);
10180 node = node->GetNext();
10181 }
10182
10183 // Do action, or delay it until end of batch.
10184 if (withUndo)
10185 buffer->EndBatchUndo();
10186
10187 return true;
10188 }
10189
10190 bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10191 {
10192 wxASSERT((startRow + noRows) < m_rowCount);
10193 if ((startRow + noRows) >= m_rowCount)
10194 return false;
10195
10196 int i, j;
10197 for (i = startRow; i < (startRow+noRows); i++)
10198 {
10199 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10200 for (j = 0; j < (int) colArray.GetCount(); j++)
10201 {
10202 wxRichTextObject* cell = colArray[j];
10203 RemoveChild(cell, true);
10204 }
10205
10206 // Keep deleting at the same position, since we move all
10207 // the others up
10208 m_cells.RemoveAt(startRow);
10209 }
10210
10211 m_rowCount = m_rowCount - noRows;
10212
10213 return true;
10214 }
10215
10216 bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10217 {
10218 wxASSERT((startCol + noCols) < m_colCount);
10219 if ((startCol + noCols) >= m_colCount)
10220 return false;
10221
10222 bool deleteRows = (noCols == m_colCount);
10223
10224 int i, j;
10225 for (i = 0; i < m_rowCount; i++)
10226 {
10227 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
10228 for (j = startCol; j < (startCol+noCols); j++)
10229 {
10230 wxRichTextObject* cell = colArray[j];
10231 RemoveChild(cell, true);
10232 }
10233
10234 if (deleteRows)
10235 m_cells.RemoveAt(0);
10236 }
10237
10238 if (deleteRows)
10239 m_rowCount = 0;
10240 m_colCount = m_colCount - noCols;
10241
10242 return true;
10243 }
10244
10245 bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10246 {
10247 wxASSERT(startRow <= m_rowCount);
10248 if (startRow > m_rowCount)
10249 return false;
10250
10251 int i, j;
10252 for (i = 0; i < noRows; i++)
10253 {
10254 int idx;
10255 if (startRow == m_rowCount)
10256 {
10257 m_cells.Add(wxRichTextObjectPtrArray());
10258 idx = m_cells.GetCount() - 1;
10259 }
10260 else
10261 {
10262 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10263 idx = startRow+i;
10264 }
10265
10266 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10267 for (j = 0; j < m_colCount; j++)
10268 {
10269 wxRichTextCell* cell = new wxRichTextCell;
10270 cell->GetAttributes() = attr;
10271
10272 AppendChild(cell);
10273 colArray.Add(cell);
10274 }
10275 }
10276
10277 m_rowCount = m_rowCount + noRows;
10278 return true;
10279 }
10280
10281 bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10282 {
10283 wxASSERT(startCol <= m_colCount);
10284 if (startCol > m_colCount)
10285 return false;
10286
10287 int i, j;
10288 for (i = 0; i < m_rowCount; i++)
10289 {
10290 wxRichTextObjectPtrArray& colArray = m_cells[i];
10291 for (j = 0; j < noCols; j++)
10292 {
10293 wxRichTextCell* cell = new wxRichTextCell;
10294 cell->GetAttributes() = attr;
10295
10296 AppendChild(cell);
10297
10298 if (startCol == m_colCount)
10299 colArray.Add(cell);
10300 else
10301 colArray.Insert(cell, startCol+j);
10302 }
10303 }
10304
10305 m_colCount = m_colCount + noCols;
10306
10307 return true;
10308 }
10309
10310 // Edit properties via a GUI
10311 bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10312 {
10313 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10314 boxDlg.SetAttributes(GetAttributes());
10315
10316 if (boxDlg.ShowModal() == wxID_OK)
10317 {
10318 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10319 return true;
10320 }
10321 else
10322 return false;
10323 }
10324
10325 /*
10326 * Module to initialise and clean up handlers
10327 */
10328
10329 class wxRichTextModule: public wxModule
10330 {
10331 DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10332 public:
10333 wxRichTextModule() {}
10334 bool OnInit()
10335 {
10336 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
10337 wxRichTextBuffer::InitStandardHandlers();
10338 wxRichTextParagraph::InitDefaultTabs();
10339
10340 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10341 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10342 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10343 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10344 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10345 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10346 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10347 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10348 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10349
10350 return true;
10351 }
10352 void OnExit()
10353 {
10354 wxRichTextBuffer::CleanUpHandlers();
10355 wxRichTextBuffer::CleanUpDrawingHandlers();
10356 wxRichTextBuffer::CleanUpFieldTypes();
10357 wxRichTextXMLHandler::ClearNodeToClassMap();
10358 wxRichTextDecimalToRoman(-1);
10359 wxRichTextParagraph::ClearDefaultTabs();
10360 wxRichTextCtrl::ClearAvailableFontNames();
10361 wxRichTextBuffer::SetRenderer(NULL);
10362 }
10363 };
10364
10365 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10366
10367
10368 // If the richtext lib is dynamically loaded after the app has already started
10369 // (such as from wxPython) then the built-in module system will not init this
10370 // module. Provide this function to do it manually.
10371 void wxRichTextModuleInit()
10372 {
10373 wxModule* module = new wxRichTextModule;
10374 module->Init();
10375 wxModule::RegisterModule(module);
10376 }
10377
10378
10379 /*!
10380 * Commands for undo/redo
10381 *
10382 */
10383
10384 wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
10385 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
10386 {
10387 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
10388 }
10389
10390 wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
10391 {
10392 }
10393
10394 wxRichTextCommand::~wxRichTextCommand()
10395 {
10396 ClearActions();
10397 }
10398
10399 void wxRichTextCommand::AddAction(wxRichTextAction* action)
10400 {
10401 if (!m_actions.Member(action))
10402 m_actions.Append(action);
10403 }
10404
10405 bool wxRichTextCommand::Do()
10406 {
10407 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
10408 {
10409 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10410 action->Do();
10411 }
10412
10413 return true;
10414 }
10415
10416 bool wxRichTextCommand::Undo()
10417 {
10418 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
10419 {
10420 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10421 action->Undo();
10422 }
10423
10424 return true;
10425 }
10426
10427 void wxRichTextCommand::ClearActions()
10428 {
10429 WX_CLEAR_LIST(wxList, m_actions);
10430 }
10431
10432 /*!
10433 * Individual action
10434 *
10435 */
10436
10437 wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10438 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10439 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
10440 {
10441 m_buffer = buffer;
10442 m_object = NULL;
10443 m_containerAddress.Create(buffer, container);
10444 m_ignoreThis = ignoreFirstTime;
10445 m_cmdId = id;
10446 m_position = -1;
10447 m_ctrl = ctrl;
10448 m_name = name;
10449 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10450 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10451 if (cmd)
10452 cmd->AddAction(this);
10453 }
10454
10455 wxRichTextAction::~wxRichTextAction()
10456 {
10457 if (m_object)
10458 delete m_object;
10459 }
10460
10461 // Returns the container that this action refers to, using the container address and top-level buffer.
10462 wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10463 {
10464 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10465 return container;
10466 }
10467
10468
10469 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10470 {
10471 // Store a list of line start character and y positions so we can figure out which area
10472 // we need to refresh
10473
10474 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10475 wxRichTextParagraphLayoutBox* container = GetContainer();
10476 wxASSERT(container != NULL);
10477 if (!container)
10478 return;
10479
10480 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10481 // If we had several actions, which only invalidate and leave layout until the
10482 // paint handler is called, then this might not be true. So we may need to switch
10483 // optimisation on only when we're simply adding text and not simultaneously
10484 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10485 // first, but of course this means we'll be doing it twice.
10486 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
10487 {
10488 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10489 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10490 int lastY = firstVisiblePt.y + clientSize.y;
10491
10492 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10493 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10494 while (node)
10495 {
10496 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10497 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10498 while (node2)
10499 {
10500 wxRichTextLine* line = node2->GetData();
10501 wxPoint pt = line->GetAbsolutePosition();
10502 wxRichTextRange range = line->GetAbsoluteRange();
10503
10504 if (pt.y > lastY)
10505 {
10506 node2 = wxRichTextLineList::compatibility_iterator();
10507 node = wxRichTextObjectList::compatibility_iterator();
10508 }
10509 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10510 {
10511 optimizationLineCharPositions.Add(range.GetStart());
10512 optimizationLineYPositions.Add(pt.y);
10513 }
10514
10515 if (node2)
10516 node2 = node2->GetNext();
10517 }
10518
10519 if (node)
10520 node = node->GetNext();
10521 }
10522 }
10523 #endif
10524 }
10525
10526 bool wxRichTextAction::Do()
10527 {
10528 m_buffer->Modify(true);
10529
10530 wxRichTextParagraphLayoutBox* container = GetContainer();
10531 wxASSERT(container != NULL);
10532 if (!container)
10533 return false;
10534
10535 switch (m_cmdId)
10536 {
10537 case wxRICHTEXT_INSERT:
10538 {
10539 // Store a list of line start character and y positions so we can figure out which area
10540 // we need to refresh
10541 wxArrayInt optimizationLineCharPositions;
10542 wxArrayInt optimizationLineYPositions;
10543
10544 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10545 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10546 #endif
10547
10548 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10549 container->UpdateRanges();
10550
10551 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10552 // Layout() would stop prematurely at the top level.
10553 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10554
10555 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
10556
10557 // Character position to caret position
10558 newCaretPosition --;
10559
10560 // Don't take into account the last newline
10561 if (m_newParagraphs.GetPartialParagraph())
10562 newCaretPosition --;
10563 else
10564 if (m_newParagraphs.GetChildren().GetCount() > 1)
10565 {
10566 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10567 if (p->GetRange().GetLength() == 1)
10568 newCaretPosition --;
10569 }
10570
10571 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
10572
10573 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10574
10575 wxRichTextEvent cmdEvent(
10576 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10577 m_ctrl ? m_ctrl->GetId() : -1);
10578 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10579 cmdEvent.SetRange(GetRange());
10580 cmdEvent.SetPosition(GetRange().GetStart());
10581 cmdEvent.SetContainer(container);
10582
10583 m_buffer->SendEvent(cmdEvent);
10584
10585 break;
10586 }
10587 case wxRICHTEXT_DELETE:
10588 {
10589 wxArrayInt optimizationLineCharPositions;
10590 wxArrayInt optimizationLineYPositions;
10591
10592 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10593 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10594 #endif
10595
10596 container->DeleteRange(GetRange());
10597 container->UpdateRanges();
10598 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10599 // Layout() would stop prematurely at the top level.
10600 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10601
10602 long caretPos = GetRange().GetStart()-1;
10603 if (caretPos >= container->GetOwnRange().GetEnd())
10604 caretPos --;
10605
10606 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10607
10608 wxRichTextEvent cmdEvent(
10609 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10610 m_ctrl ? m_ctrl->GetId() : -1);
10611 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10612 cmdEvent.SetRange(GetRange());
10613 cmdEvent.SetPosition(GetRange().GetStart());
10614 cmdEvent.SetContainer(container);
10615
10616 m_buffer->SendEvent(cmdEvent);
10617
10618 break;
10619 }
10620 case wxRICHTEXT_CHANGE_STYLE:
10621 case wxRICHTEXT_CHANGE_PROPERTIES:
10622 {
10623 ApplyParagraphs(GetNewParagraphs());
10624
10625 // Invalidate the whole buffer if there were floating objects
10626 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10627 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10628 else
10629 {
10630 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10631 // Layout() would stop prematurely at the top level.
10632 container->InvalidateHierarchy(GetRange());
10633 }
10634
10635 UpdateAppearance(GetPosition());
10636
10637 wxRichTextEvent cmdEvent(
10638 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10639 m_ctrl ? m_ctrl->GetId() : -1);
10640 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10641 cmdEvent.SetRange(GetRange());
10642 cmdEvent.SetPosition(GetRange().GetStart());
10643 cmdEvent.SetContainer(container);
10644
10645 m_buffer->SendEvent(cmdEvent);
10646
10647 break;
10648 }
10649 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10650 {
10651 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10652 if (obj)
10653 {
10654 wxRichTextAttr oldAttr = obj->GetAttributes();
10655 obj->GetAttributes() = m_attributes;
10656 m_attributes = oldAttr;
10657 }
10658
10659 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10660 // Layout() would stop prematurely at the top level.
10661 // Invalidate the whole buffer if there were floating objects
10662 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10663 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10664 else
10665 container->InvalidateHierarchy(GetRange());
10666
10667 UpdateAppearance(GetPosition());
10668
10669 wxRichTextEvent cmdEvent(
10670 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10671 m_ctrl ? m_ctrl->GetId() : -1);
10672 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10673 cmdEvent.SetRange(GetRange());
10674 cmdEvent.SetPosition(GetRange().GetStart());
10675 cmdEvent.SetContainer(container);
10676
10677 m_buffer->SendEvent(cmdEvent);
10678
10679 break;
10680 }
10681 case wxRICHTEXT_CHANGE_OBJECT:
10682 {
10683 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10684 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10685 if (obj && m_object)
10686 {
10687 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10688 if (node)
10689 {
10690 wxRichTextObject* obj = node->GetData();
10691 node->SetData(m_object);
10692 m_object = obj;
10693 }
10694 }
10695
10696 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10697 // Layout() would stop prematurely at the top level.
10698 // Invalidate the whole buffer if there were floating objects
10699 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10700 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10701 else
10702 container->InvalidateHierarchy(GetRange());
10703
10704 UpdateAppearance(GetPosition());
10705
10706 // TODO: send new kind of modification event
10707
10708 break;
10709 }
10710 default:
10711 break;
10712 }
10713
10714 return true;
10715 }
10716
10717 bool wxRichTextAction::Undo()
10718 {
10719 m_buffer->Modify(true);
10720
10721 wxRichTextParagraphLayoutBox* container = GetContainer();
10722 wxASSERT(container != NULL);
10723 if (!container)
10724 return false;
10725
10726 switch (m_cmdId)
10727 {
10728 case wxRICHTEXT_INSERT:
10729 {
10730 wxArrayInt optimizationLineCharPositions;
10731 wxArrayInt optimizationLineYPositions;
10732
10733 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10734 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10735 #endif
10736
10737 container->DeleteRange(GetRange());
10738 container->UpdateRanges();
10739
10740 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10741 // Layout() would stop prematurely at the top level.
10742 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10743
10744 long newCaretPosition = GetPosition() - 1;
10745
10746 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10747
10748 wxRichTextEvent cmdEvent(
10749 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10750 m_ctrl ? m_ctrl->GetId() : -1);
10751 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10752 cmdEvent.SetRange(GetRange());
10753 cmdEvent.SetPosition(GetRange().GetStart());
10754 cmdEvent.SetContainer(container);
10755
10756 m_buffer->SendEvent(cmdEvent);
10757
10758 break;
10759 }
10760 case wxRICHTEXT_DELETE:
10761 {
10762 wxArrayInt optimizationLineCharPositions;
10763 wxArrayInt optimizationLineYPositions;
10764
10765 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10766 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10767 #endif
10768
10769 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10770 container->UpdateRanges();
10771
10772 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10773 // Layout() would stop prematurely at the top level.
10774 container->InvalidateHierarchy(GetRange());
10775
10776 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10777
10778 wxRichTextEvent cmdEvent(
10779 wxEVT_COMMAND_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_CHANGE_STYLE:
10791 case wxRICHTEXT_CHANGE_PROPERTIES:
10792 {
10793 ApplyParagraphs(GetOldParagraphs());
10794 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10795 // Layout() would stop prematurely at the top level.
10796 container->InvalidateHierarchy(GetRange());
10797
10798 UpdateAppearance(GetPosition());
10799
10800 wxRichTextEvent cmdEvent(
10801 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10802 m_ctrl ? m_ctrl->GetId() : -1);
10803 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10804 cmdEvent.SetRange(GetRange());
10805 cmdEvent.SetPosition(GetRange().GetStart());
10806 cmdEvent.SetContainer(container);
10807
10808 m_buffer->SendEvent(cmdEvent);
10809
10810 break;
10811 }
10812 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10813 case wxRICHTEXT_CHANGE_OBJECT:
10814 {
10815 return Do();
10816 }
10817 default:
10818 break;
10819 }
10820
10821 return true;
10822 }
10823
10824 /// Update the control appearance
10825 void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
10826 {
10827 wxRichTextParagraphLayoutBox* container = GetContainer();
10828 wxASSERT(container != NULL);
10829 if (!container)
10830 return;
10831
10832 if (m_ctrl)
10833 {
10834 m_ctrl->SetFocusObject(container);
10835 m_ctrl->SetCaretPosition(caretPosition);
10836
10837 if (!m_ctrl->IsFrozen())
10838 {
10839 wxRect containerRect = container->GetRect();
10840
10841 m_ctrl->LayoutContent();
10842
10843 // Refresh everything if there were floating objects or the container changed size
10844 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10845 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
10846 {
10847 m_ctrl->Refresh(false);
10848 }
10849 else
10850
10851 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10852 // Find refresh rectangle if we are in a position to optimise refresh
10853 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10854 {
10855 size_t i;
10856
10857 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10858 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10859
10860 // Start/end positions
10861 int firstY = 0;
10862 int lastY = firstVisiblePt.y + clientSize.y;
10863
10864 bool foundEnd = false;
10865
10866 // position offset - how many characters were inserted
10867 int positionOffset = GetRange().GetLength();
10868
10869 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10870 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10871 positionOffset = - positionOffset;
10872
10873 // find the first line which is being drawn at the same position as it was
10874 // before. Since we're talking about a simple insertion, we can assume
10875 // that the rest of the window does not need to be redrawn.
10876
10877 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10878 // Since we support floating layout, we should redraw the whole para instead of just
10879 // the first line touching the invalid range.
10880 if (para)
10881 {
10882 firstY = para->GetPosition().y;
10883 }
10884
10885 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10886 while (node)
10887 {
10888 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10889 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10890 while (node2)
10891 {
10892 wxRichTextLine* line = node2->GetData();
10893 wxPoint pt = line->GetAbsolutePosition();
10894 wxRichTextRange range = line->GetAbsoluteRange();
10895
10896 // we want to find the first line that is in the same position
10897 // as before. This will mean we're at the end of the changed text.
10898
10899 if (pt.y > lastY) // going past the end of the window, no more info
10900 {
10901 node2 = wxRichTextLineList::compatibility_iterator();
10902 node = wxRichTextObjectList::compatibility_iterator();
10903 }
10904 // Detect last line in the buffer
10905 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10906 {
10907 // If deleting text, make sure we refresh below as well as above
10908 if (positionOffset >= 0)
10909 {
10910 foundEnd = true;
10911 lastY = pt.y + line->GetSize().y;
10912 }
10913
10914 node2 = wxRichTextLineList::compatibility_iterator();
10915 node = wxRichTextObjectList::compatibility_iterator();
10916
10917 break;
10918 }
10919 else
10920 {
10921 // search for this line being at the same position as before
10922 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10923 {
10924 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10925 ((*optimizationLineYPositions)[i] == pt.y))
10926 {
10927 // Stop, we're now the same as we were
10928 foundEnd = true;
10929
10930 lastY = pt.y;
10931
10932 node2 = wxRichTextLineList::compatibility_iterator();
10933 node = wxRichTextObjectList::compatibility_iterator();
10934
10935 break;
10936 }
10937 }
10938 }
10939
10940 if (node2)
10941 node2 = node2->GetNext();
10942 }
10943
10944 if (node)
10945 node = node->GetNext();
10946 }
10947
10948 firstY = wxMax(firstVisiblePt.y, firstY);
10949 if (!foundEnd)
10950 lastY = firstVisiblePt.y + clientSize.y;
10951
10952 // Convert to device coordinates
10953 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
10954 m_ctrl->RefreshRect(rect);
10955 }
10956 else
10957 #endif
10958 m_ctrl->Refresh(false);
10959
10960 m_ctrl->PositionCaret();
10961
10962 // This causes styles to persist when doing programmatic
10963 // content creation except when Freeze/Thaw is used, so
10964 // disable this and check for the consequences.
10965 // m_ctrl->SetDefaultStyleToCursorStyle();
10966
10967 if (sendUpdateEvent)
10968 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
10969 }
10970 }
10971 }
10972
10973 /// Replace the buffer paragraphs with the new ones.
10974 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
10975 {
10976 wxRichTextParagraphLayoutBox* container = GetContainer();
10977 wxASSERT(container != NULL);
10978 if (!container)
10979 return;
10980
10981 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
10982 while (node)
10983 {
10984 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
10985 wxASSERT (para != NULL);
10986
10987 // We'll replace the existing paragraph by finding the paragraph at this position,
10988 // delete its node data, and setting a copy as the new node data.
10989 // TODO: make more efficient by simply swapping old and new paragraph objects.
10990
10991 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
10992 if (existingPara)
10993 {
10994 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
10995 if (bufferParaNode)
10996 {
10997 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
10998 newPara->SetParent(container);
10999
11000 bufferParaNode->SetData(newPara);
11001
11002 delete existingPara;
11003 }
11004 }
11005
11006 node = node->GetNext();
11007 }
11008 }
11009
11010
11011 /*!
11012 * wxRichTextRange
11013 * This stores beginning and end positions for a range of data.
11014 */
11015
11016 WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11017
11018 /// Limit this range to be within 'range'
11019 bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11020 {
11021 if (m_start < range.m_start)
11022 m_start = range.m_start;
11023
11024 if (m_end > range.m_end)
11025 m_end = range.m_end;
11026
11027 return true;
11028 }
11029
11030 /*!
11031 * wxRichTextImage implementation
11032 * This object represents an image.
11033 */
11034
11035 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
11036
11037 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11038 wxRichTextObject(parent)
11039 {
11040 Init();
11041 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
11042 if (charStyle)
11043 SetAttributes(*charStyle);
11044 }
11045
11046 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11047 wxRichTextObject(parent)
11048 {
11049 Init();
11050 m_imageBlock = imageBlock;
11051 if (charStyle)
11052 SetAttributes(*charStyle);
11053 }
11054
11055 void wxRichTextImage::Init()
11056 {
11057 m_originalImageSize = wxSize(-1, -1);
11058 }
11059
11060 /// Create a cached image at the required size
11061 bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
11062 {
11063 if (!m_imageBlock.IsOk())
11064 return false;
11065
11066 // If we have an original image size, use that to compute the cached bitmap size
11067 // instead of loading the image each time. This way we can avoid loading
11068 // the image so long as the new cached bitmap size hasn't changed.
11069
11070 wxImage image;
11071 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
11072 {
11073 m_imageCache = wxNullBitmap;
11074
11075 m_imageBlock.Load(image);
11076 if (!image.IsOk())
11077 return false;
11078
11079 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11080 }
11081
11082 int width = m_originalImageSize.GetWidth();
11083 int height = m_originalImageSize.GetHeight();
11084
11085 int parentWidth = 0;
11086 int parentHeight = 0;
11087
11088 int maxWidth = -1;
11089 int maxHeight = -1;
11090
11091 wxRichTextBuffer* buffer = GetBuffer();
11092 if (buffer)
11093 {
11094 wxSize sz;
11095 if (buffer->GetRichTextCtrl())
11096 {
11097 // Subtract borders
11098 sz = buffer->GetRichTextCtrl()->GetClientSize();
11099
11100 // Use a minimum size to stop images becoming very small
11101 sz.x = wxMax(sz.x, 100);
11102 sz.y = wxMax(sz.y, 100);
11103
11104 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11105 marginRect = wxRect(0, 0, sz.x, sz.y);
11106 buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11107
11108 sz = contentRect.GetSize();
11109
11110 // Start with a maximum width of the control size, even if not specified by the content,
11111 // to minimize the amount of picture overlapping the right-hand side
11112 maxWidth = sz.x;
11113 }
11114 else
11115 sz = buffer->GetCachedSize();
11116 parentWidth = sz.GetWidth();
11117 parentHeight = sz.GetHeight();
11118 }
11119
11120 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11121 {
11122 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11123 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11124 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11125 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11126 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11127 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11128 }
11129
11130 // Limit to max width
11131
11132 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11133 {
11134 int mw = -1;
11135
11136 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11137 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11138 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11139 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11140 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11141 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11142
11143 // If we already have a smaller max width due to the constraints of the control size,
11144 // don't use the larger max width.
11145 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11146 maxWidth = mw;
11147 }
11148
11149 if (maxWidth > 0 && width > maxWidth)
11150 width = maxWidth;
11151
11152 // Preserve the aspect ratio
11153 if (width != m_originalImageSize.GetWidth())
11154 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11155
11156 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11157 {
11158 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11159 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11160 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11161 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11162 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11163 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11164
11165 // Preserve the aspect ratio
11166 if (height != m_originalImageSize.GetHeight())
11167 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11168 }
11169
11170 // Limit to max height
11171
11172 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11173 {
11174 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11175 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11176 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11177 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11178 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11179 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11180 }
11181
11182 if (maxHeight > 0 && height > maxHeight)
11183 {
11184 height = maxHeight;
11185
11186 // Preserve the aspect ratio
11187 if (height != m_originalImageSize.GetHeight())
11188 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11189 }
11190
11191 // Prevent the use of zero size
11192 width = wxMax(1, width);
11193 height = wxMax(1, height);
11194
11195 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11196 {
11197 // Do nothing, we didn't need to change the image cache
11198 }
11199 else
11200 {
11201 if (!image.IsOk())
11202 {
11203 m_imageBlock.Load(image);
11204 if (!image.IsOk())
11205 return false;
11206 }
11207
11208 if (image.GetWidth() == width && image.GetHeight() == height)
11209 m_imageCache = wxBitmap(image);
11210 else
11211 {
11212 // If the original width and height is small, e.g. 400 or below,
11213 // scale up and then down to improve image quality. This can make
11214 // a big difference, with not much performance hit.
11215 int upscaleThreshold = 400;
11216 wxImage img;
11217 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11218 {
11219 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11220 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11221 }
11222 else
11223 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11224 m_imageCache = wxBitmap(img);
11225 }
11226 }
11227
11228 return m_imageCache.IsOk();
11229 }
11230
11231 /// Draw the item
11232 bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
11233 {
11234 if (!IsShown())
11235 return true;
11236
11237 // Don't need cached size AFAIK
11238 // wxSize size = GetCachedSize();
11239 if (!LoadImageCache(dc))
11240 return false;
11241
11242 wxRichTextAttr attr(GetAttributes());
11243 context.ApplyVirtualAttributes(attr, this);
11244
11245 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
11246
11247 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11248 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11249 marginRect = rect; // outer rectangle, will calculate contentRect
11250 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11251
11252 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
11253
11254 if (selection.WithinSelection(GetRange().GetStart(), this))
11255 {
11256 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11257 wxCheckSetPen(dc, *wxBLACK_PEN);
11258 dc.SetLogicalFunction(wxINVERT);
11259 dc.DrawRectangle(contentRect);
11260 dc.SetLogicalFunction(wxCOPY);
11261 }
11262
11263 return true;
11264 }
11265
11266 /// Lay the item out
11267 bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
11268 {
11269 if (!LoadImageCache(dc))
11270 return false;
11271
11272 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11273 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11274 contentRect = wxRect(wxPoint(0,0), imageSize);
11275
11276 wxRichTextAttr attr(GetAttributes());
11277 context.ApplyVirtualAttributes(attr, this);
11278
11279 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11280
11281 wxSize overallSize = marginRect.GetSize();
11282
11283 SetCachedSize(overallSize);
11284 SetMaxSize(overallSize);
11285 SetMinSize(overallSize);
11286 SetPosition(rect.GetPosition());
11287
11288 return true;
11289 }
11290
11291 /// Get/set the object size for the given range. Returns false if the range
11292 /// is invalid for this object.
11293 bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
11294 {
11295 if (!range.IsWithin(GetRange()))
11296 return false;
11297
11298 if (!((wxRichTextImage*)this)->LoadImageCache(dc))
11299 {
11300 size.x = 0; size.y = 0;
11301 if (partialExtents)
11302 partialExtents->Add(0);
11303 return false;
11304 }
11305
11306 wxRichTextAttr attr(GetAttributes());
11307 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11308
11309 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11310 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11311 contentRect = wxRect(wxPoint(0,0), imageSize);
11312 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11313
11314 wxSize overallSize = marginRect.GetSize();
11315
11316 if (partialExtents)
11317 partialExtents->Add(overallSize.x);
11318
11319 size = overallSize;
11320
11321 return true;
11322 }
11323
11324 // Get the 'natural' size for an object. For an image, it would be the
11325 // image size.
11326 wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11327 {
11328 wxTextAttrSize size;
11329 if (GetImageCache().IsOk())
11330 {
11331 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11332 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11333 }
11334 return size;
11335 }
11336
11337
11338 /// Copy
11339 void wxRichTextImage::Copy(const wxRichTextImage& obj)
11340 {
11341 wxRichTextObject::Copy(obj);
11342
11343 m_imageBlock = obj.m_imageBlock;
11344 m_originalImageSize = obj.m_originalImageSize;
11345 }
11346
11347 /// Edit properties via a GUI
11348 bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11349 {
11350 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11351 imageDlg.SetAttributes(GetAttributes());
11352
11353 if (imageDlg.ShowModal() == wxID_OK)
11354 {
11355 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11356 // indeterminate in the object.
11357 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
11358 return true;
11359 }
11360 else
11361 return false;
11362 }
11363
11364 /*!
11365 * Utilities
11366 *
11367 */
11368
11369 /// Compare two attribute objects
11370 bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
11371 {
11372 return (attr1 == attr2);
11373 }
11374
11375 /// Compare tabs
11376 bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11377 {
11378 if (tabs1.GetCount() != tabs2.GetCount())
11379 return false;
11380
11381 size_t i;
11382 for (i = 0; i < tabs1.GetCount(); i++)
11383 {
11384 if (tabs1[i] != tabs2[i])
11385 return false;
11386 }
11387 return true;
11388 }
11389
11390 bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
11391 {
11392 return destStyle.Apply(style, compareWith);
11393 }
11394
11395 // Remove attributes
11396 bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
11397 {
11398 return destStyle.RemoveStyle(style);
11399 }
11400
11401 /// Combine two bitlists, specifying the bits of interest with separate flags.
11402 bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11403 {
11404 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
11405 }
11406
11407 /// Compare two bitlists
11408 bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11409 {
11410 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
11411 }
11412
11413 /// Split into paragraph and character styles
11414 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
11415 {
11416 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
11417 }
11418
11419 /// Convert a decimal to Roman numerals
11420 wxString wxRichTextDecimalToRoman(long n)
11421 {
11422 static wxArrayInt decimalNumbers;
11423 static wxArrayString romanNumbers;
11424
11425 // Clean up arrays
11426 if (n == -1)
11427 {
11428 decimalNumbers.Clear();
11429 romanNumbers.Clear();
11430 return wxEmptyString;
11431 }
11432
11433 if (decimalNumbers.GetCount() == 0)
11434 {
11435 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11436
11437 wxRichTextAddDecRom(1000, wxT("M"));
11438 wxRichTextAddDecRom(900, wxT("CM"));
11439 wxRichTextAddDecRom(500, wxT("D"));
11440 wxRichTextAddDecRom(400, wxT("CD"));
11441 wxRichTextAddDecRom(100, wxT("C"));
11442 wxRichTextAddDecRom(90, wxT("XC"));
11443 wxRichTextAddDecRom(50, wxT("L"));
11444 wxRichTextAddDecRom(40, wxT("XL"));
11445 wxRichTextAddDecRom(10, wxT("X"));
11446 wxRichTextAddDecRom(9, wxT("IX"));
11447 wxRichTextAddDecRom(5, wxT("V"));
11448 wxRichTextAddDecRom(4, wxT("IV"));
11449 wxRichTextAddDecRom(1, wxT("I"));
11450 }
11451
11452 int i = 0;
11453 wxString roman;
11454
11455 while (n > 0 && i < 13)
11456 {
11457 if (n >= decimalNumbers[i])
11458 {
11459 n -= decimalNumbers[i];
11460 roman += romanNumbers[i];
11461 }
11462 else
11463 {
11464 i ++;
11465 }
11466 }
11467 if (roman.IsEmpty())
11468 roman = wxT("0");
11469 return roman;
11470 }
11471
11472 /*!
11473 * wxRichTextFileHandler
11474 * Base class for file handlers
11475 */
11476
11477 IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
11478
11479 #if wxUSE_FFILE && wxUSE_STREAMS
11480 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
11481 {
11482 wxFFileInputStream stream(filename);
11483 if (stream.IsOk())
11484 return LoadFile(buffer, stream);
11485
11486 return false;
11487 }
11488
11489 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11490 {
11491 wxFFileOutputStream stream(filename);
11492 if (stream.IsOk())
11493 return SaveFile(buffer, stream);
11494
11495 return false;
11496 }
11497 #endif // wxUSE_FFILE && wxUSE_STREAMS
11498
11499 /// Can we handle this filename (if using files)? By default, checks the extension.
11500 bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11501 {
11502 wxString path, file, ext;
11503 wxFileName::SplitPath(filename, & path, & file, & ext);
11504
11505 return (ext.Lower() == GetExtension());
11506 }
11507
11508 /*!
11509 * wxRichTextTextHandler
11510 * Plain text handler
11511 */
11512
11513 IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
11514
11515 #if wxUSE_STREAMS
11516 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11517 {
11518 if (!stream.IsOk())
11519 return false;
11520
11521 wxString str;
11522 int lastCh = 0;
11523
11524 while (!stream.Eof())
11525 {
11526 int ch = stream.GetC();
11527
11528 if (!stream.Eof())
11529 {
11530 if (ch == 10 && lastCh != 13)
11531 str += wxT('\n');
11532
11533 if (ch > 0 && ch != 10)
11534 str += wxChar(ch);
11535
11536 lastCh = ch;
11537 }
11538 }
11539
11540 buffer->ResetAndClearCommands();
11541 buffer->Clear();
11542 buffer->AddParagraphs(str);
11543 buffer->UpdateRanges();
11544
11545 return true;
11546 }
11547
11548 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11549 {
11550 if (!stream.IsOk())
11551 return false;
11552
11553 wxString text = buffer->GetText();
11554
11555 wxString newLine = wxRichTextLineBreakChar;
11556 text.Replace(newLine, wxT("\n"));
11557
11558 wxCharBuffer buf = text.ToAscii();
11559
11560 stream.Write((const char*) buf, text.length());
11561 return true;
11562 }
11563 #endif // wxUSE_STREAMS
11564
11565 /*
11566 * Stores information about an image, in binary in-memory form
11567 */
11568
11569 wxRichTextImageBlock::wxRichTextImageBlock()
11570 {
11571 Init();
11572 }
11573
11574 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11575 {
11576 Init();
11577 Copy(block);
11578 }
11579
11580 wxRichTextImageBlock::~wxRichTextImageBlock()
11581 {
11582 wxDELETEA(m_data);
11583 }
11584
11585 void wxRichTextImageBlock::Init()
11586 {
11587 m_data = NULL;
11588 m_dataSize = 0;
11589 m_imageType = wxBITMAP_TYPE_INVALID;
11590 }
11591
11592 void wxRichTextImageBlock::Clear()
11593 {
11594 wxDELETEA(m_data);
11595 m_dataSize = 0;
11596 m_imageType = wxBITMAP_TYPE_INVALID;
11597 }
11598
11599
11600 // Load the original image into a memory block.
11601 // If the image is not a JPEG, we must convert it into a JPEG
11602 // to conserve space.
11603 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11604 // load the image a 2nd time.
11605
11606 bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11607 wxImage& image, bool convertToJPEG)
11608 {
11609 m_imageType = imageType;
11610
11611 wxString filenameToRead(filename);
11612 bool removeFile = false;
11613
11614 if (imageType == wxBITMAP_TYPE_INVALID)
11615 return false; // Could not determine image type
11616
11617 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11618 {
11619 wxString tempFile =
11620 wxFileName::CreateTempFileName(_("image"));
11621
11622 wxASSERT(!tempFile.IsEmpty());
11623
11624 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11625 filenameToRead = tempFile;
11626 removeFile = true;
11627
11628 m_imageType = wxBITMAP_TYPE_JPEG;
11629 }
11630 wxFile file;
11631 if (!file.Open(filenameToRead))
11632 return false;
11633
11634 m_dataSize = (size_t) file.Length();
11635 file.Close();
11636
11637 if (m_data)
11638 delete[] m_data;
11639 m_data = ReadBlock(filenameToRead, m_dataSize);
11640
11641 if (removeFile)
11642 wxRemoveFile(filenameToRead);
11643
11644 return (m_data != NULL);
11645 }
11646
11647 // Make an image block from the wxImage in the given
11648 // format.
11649 bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
11650 {
11651 image.SetOption(wxT("quality"), quality);
11652
11653 if (imageType == wxBITMAP_TYPE_INVALID)
11654 return false; // Could not determine image type
11655
11656 return DoMakeImageBlock(image, imageType);
11657 }
11658
11659 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11660 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11661 {
11662 if (imageType == wxBITMAP_TYPE_INVALID)
11663 return false; // Could not determine image type
11664
11665 return DoMakeImageBlock(image, imageType);
11666 }
11667
11668 // Makes the image block
11669 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11670 {
11671 wxMemoryOutputStream memStream;
11672 if (!image.SaveFile(memStream, imageType))
11673 {
11674 return false;
11675 }
11676
11677 unsigned char* block = new unsigned char[memStream.GetSize()];
11678 if (!block)
11679 return false;
11680
11681 if (m_data)
11682 delete[] m_data;
11683 m_data = block;
11684
11685 m_imageType = imageType;
11686 m_dataSize = memStream.GetSize();
11687
11688 memStream.CopyTo(m_data, m_dataSize);
11689
11690 return (m_data != NULL);
11691 }
11692
11693 // Write to a file
11694 bool wxRichTextImageBlock::Write(const wxString& filename)
11695 {
11696 return WriteBlock(filename, m_data, m_dataSize);
11697 }
11698
11699 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11700 {
11701 m_imageType = block.m_imageType;
11702 wxDELETEA(m_data);
11703 m_dataSize = block.m_dataSize;
11704 if (m_dataSize == 0)
11705 return;
11706
11707 m_data = new unsigned char[m_dataSize];
11708 unsigned int i;
11709 for (i = 0; i < m_dataSize; i++)
11710 m_data[i] = block.m_data[i];
11711 }
11712
11713 //// Operators
11714 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11715 {
11716 Copy(block);
11717 }
11718
11719 // Load a wxImage from the block
11720 bool wxRichTextImageBlock::Load(wxImage& image)
11721 {
11722 if (!m_data)
11723 return false;
11724
11725 // Read in the image.
11726 #if wxUSE_STREAMS
11727 wxMemoryInputStream mstream(m_data, m_dataSize);
11728 bool success = image.LoadFile(mstream, GetImageType());
11729 #else
11730 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11731 wxASSERT(!tempFile.IsEmpty());
11732
11733 if (!WriteBlock(tempFile, m_data, m_dataSize))
11734 {
11735 return false;
11736 }
11737 success = image.LoadFile(tempFile, GetImageType());
11738 wxRemoveFile(tempFile);
11739 #endif
11740
11741 return success;
11742 }
11743
11744 // Write data in hex to a stream
11745 bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11746 {
11747 if (m_dataSize == 0)
11748 return true;
11749
11750 int bufSize = 100000;
11751 if (int(2*m_dataSize) < bufSize)
11752 bufSize = 2*m_dataSize;
11753 char* buf = new char[bufSize+1];
11754
11755 int left = m_dataSize;
11756 int n, i, j;
11757 j = 0;
11758 while (left > 0)
11759 {
11760 if (left*2 > bufSize)
11761 {
11762 n = bufSize; left -= (bufSize/2);
11763 }
11764 else
11765 {
11766 n = left*2; left = 0;
11767 }
11768
11769 char* b = buf;
11770 for (i = 0; i < (n/2); i++)
11771 {
11772 wxDecToHex(m_data[j], b, b+1);
11773 b += 2; j ++;
11774 }
11775
11776 buf[n] = 0;
11777 stream.Write((const char*) buf, n);
11778 }
11779 delete[] buf;
11780 return true;
11781 }
11782
11783 // Read data in hex from a stream
11784 bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
11785 {
11786 int dataSize = length/2;
11787
11788 if (m_data)
11789 delete[] m_data;
11790
11791 // create a null terminated temporary string:
11792 char str[3];
11793 str[2] = '\0';
11794
11795 m_data = new unsigned char[dataSize];
11796 int i;
11797 for (i = 0; i < dataSize; i ++)
11798 {
11799 str[0] = (char)stream.GetC();
11800 str[1] = (char)stream.GetC();
11801
11802 m_data[i] = (unsigned char)wxHexToDec(str);
11803 }
11804
11805 m_dataSize = dataSize;
11806 m_imageType = imageType;
11807
11808 return true;
11809 }
11810
11811 // Allocate and read from stream as a block of memory
11812 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11813 {
11814 unsigned char* block = new unsigned char[size];
11815 if (!block)
11816 return NULL;
11817
11818 stream.Read(block, size);
11819
11820 return block;
11821 }
11822
11823 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11824 {
11825 wxFileInputStream stream(filename);
11826 if (!stream.IsOk())
11827 return NULL;
11828
11829 return ReadBlock(stream, size);
11830 }
11831
11832 // Write memory block to stream
11833 bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11834 {
11835 stream.Write((void*) block, size);
11836 return stream.IsOk();
11837
11838 }
11839
11840 // Write memory block to file
11841 bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11842 {
11843 wxFileOutputStream outStream(filename);
11844 if (!outStream.IsOk())
11845 return false;
11846
11847 return WriteBlock(outStream, block, size);
11848 }
11849
11850 // Gets the extension for the block's type
11851 wxString wxRichTextImageBlock::GetExtension() const
11852 {
11853 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11854 if (handler)
11855 return handler->GetExtension();
11856 else
11857 return wxEmptyString;
11858 }
11859
11860 #if wxUSE_DATAOBJ
11861
11862 /*!
11863 * The data object for a wxRichTextBuffer
11864 */
11865
11866 const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11867
11868 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11869 {
11870 m_richTextBuffer = richTextBuffer;
11871
11872 // this string should uniquely identify our format, but is otherwise
11873 // arbitrary
11874 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11875
11876 SetFormat(m_formatRichTextBuffer);
11877 }
11878
11879 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11880 {
11881 delete m_richTextBuffer;
11882 }
11883
11884 // after a call to this function, the richTextBuffer is owned by the caller and it
11885 // is responsible for deleting it!
11886 wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11887 {
11888 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11889 m_richTextBuffer = NULL;
11890
11891 return richTextBuffer;
11892 }
11893
11894 wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11895 {
11896 return m_formatRichTextBuffer;
11897 }
11898
11899 size_t wxRichTextBufferDataObject::GetDataSize() const
11900 {
11901 if (!m_richTextBuffer)
11902 return 0;
11903
11904 wxString bufXML;
11905
11906 {
11907 wxStringOutputStream stream(& bufXML);
11908 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11909 {
11910 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11911 return 0;
11912 }
11913 }
11914
11915 #if wxUSE_UNICODE
11916 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11917 return strlen(buffer) + 1;
11918 #else
11919 return bufXML.Length()+1;
11920 #endif
11921 }
11922
11923 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11924 {
11925 if (!pBuf || !m_richTextBuffer)
11926 return false;
11927
11928 wxString bufXML;
11929
11930 {
11931 wxStringOutputStream stream(& bufXML);
11932 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11933 {
11934 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11935 return 0;
11936 }
11937 }
11938
11939 #if wxUSE_UNICODE
11940 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11941 size_t len = strlen(buffer);
11942 memcpy((char*) pBuf, (const char*) buffer, len);
11943 ((char*) pBuf)[len] = 0;
11944 #else
11945 size_t len = bufXML.Length();
11946 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11947 ((char*) pBuf)[len] = 0;
11948 #endif
11949
11950 return true;
11951 }
11952
11953 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
11954 {
11955 wxDELETE(m_richTextBuffer);
11956
11957 wxString bufXML((const char*) buf, wxConvUTF8);
11958
11959 m_richTextBuffer = new wxRichTextBuffer;
11960
11961 wxStringInputStream stream(bufXML);
11962 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
11963 {
11964 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
11965
11966 wxDELETE(m_richTextBuffer);
11967
11968 return false;
11969 }
11970 return true;
11971 }
11972
11973 #endif
11974 // wxUSE_DATAOBJ
11975
11976
11977 /*
11978 * wxRichTextFontTable
11979 * Manages quick access to a pool of fonts for rendering rich text
11980 */
11981
11982 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
11983
11984 class wxRichTextFontTableData: public wxObjectRefData
11985 {
11986 public:
11987 wxRichTextFontTableData() {}
11988
11989 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
11990
11991 wxRichTextFontTableHashMap m_hashMap;
11992 };
11993
11994 wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
11995 {
11996 wxString facename(fontSpec.GetFontFaceName());
11997
11998 int fontSize = fontSpec.GetFontSize();
11999 if (fontScale != 1.0)
12000 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12001
12002 wxString units;
12003 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12004 units = wxT("px");
12005 else
12006 units = wxT("pt");
12007 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12008 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12009 facename.c_str(), (int) fontSpec.GetFontEncoding());
12010
12011 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
12012 if ( entry == m_hashMap.end() )
12013 {
12014 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12015 {
12016 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
12017 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12018 font.SetStrikethrough(true);
12019 m_hashMap[spec] = font;
12020 return font;
12021 }
12022 else
12023 {
12024 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
12025 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12026 font.SetStrikethrough(true);
12027
12028 m_hashMap[spec] = font;
12029 return font;
12030 }
12031 }
12032 else
12033 {
12034 return entry->second;
12035 }
12036 }
12037
12038 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12039
12040 wxRichTextFontTable::wxRichTextFontTable()
12041 {
12042 m_refData = new wxRichTextFontTableData;
12043 m_fontScale = 1.0;
12044 }
12045
12046 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
12047 : wxObject()
12048 {
12049 (*this) = table;
12050 }
12051
12052 wxRichTextFontTable::~wxRichTextFontTable()
12053 {
12054 UnRef();
12055 }
12056
12057 bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12058 {
12059 return (m_refData == table.m_refData);
12060 }
12061
12062 void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12063 {
12064 Ref(table);
12065 m_fontScale = table.m_fontScale;
12066 }
12067
12068 wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
12069 {
12070 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12071 if (data)
12072 return data->FindFont(fontSpec, m_fontScale);
12073 else
12074 return wxFont();
12075 }
12076
12077 void wxRichTextFontTable::Clear()
12078 {
12079 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12080 if (data)
12081 data->m_hashMap.clear();
12082 }
12083
12084 void wxRichTextFontTable::SetFontScale(double fontScale)
12085 {
12086 if (fontScale != m_fontScale)
12087 Clear();
12088 m_fontScale = fontScale;
12089 }
12090
12091 // wxTextBoxAttr
12092
12093 void wxTextBoxAttr::Reset()
12094 {
12095 m_flags = 0;
12096 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12097 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12098 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12099 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
12100 m_boxStyleName = wxEmptyString;
12101
12102 m_margins.Reset();
12103 m_padding.Reset();
12104 m_position.Reset();
12105
12106 m_size.Reset();
12107 m_minSize.Reset();
12108 m_maxSize.Reset();
12109
12110 m_border.Reset();
12111 m_outline.Reset();
12112 }
12113
12114 // Equality test
12115 bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12116 {
12117 return (
12118 m_flags == attr.m_flags &&
12119 m_floatMode == attr.m_floatMode &&
12120 m_clearMode == attr.m_clearMode &&
12121 m_collapseMode == attr.m_collapseMode &&
12122 m_verticalAlignment == attr.m_verticalAlignment &&
12123
12124 m_margins == attr.m_margins &&
12125 m_padding == attr.m_padding &&
12126 m_position == attr.m_position &&
12127
12128 m_size == attr.m_size &&
12129 m_minSize == attr.m_minSize &&
12130 m_maxSize == attr.m_maxSize &&
12131
12132 m_border == attr.m_border &&
12133 m_outline == attr.m_outline &&
12134
12135 m_boxStyleName == attr.m_boxStyleName
12136 );
12137 }
12138
12139 // Partial equality test
12140 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
12141 {
12142 if (!weakTest &&
12143 ((!HasFloatMode() && attr.HasFloatMode()) ||
12144 (!HasClearMode() && attr.HasClearMode()) ||
12145 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12146 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12147 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12148 {
12149 return false;
12150 }
12151 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12152 return false;
12153
12154 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12155 return false;
12156
12157 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12158 return false;
12159
12160 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12161 return false;
12162
12163 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12164 return false;
12165
12166 // Position
12167
12168 if (!m_position.EqPartial(attr.m_position, weakTest))
12169 return false;
12170
12171 // Size
12172
12173 if (!m_size.EqPartial(attr.m_size, weakTest))
12174 return false;
12175 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
12176 return false;
12177 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
12178 return false;
12179
12180 // Margins
12181
12182 if (!m_margins.EqPartial(attr.m_margins, weakTest))
12183 return false;
12184
12185 // Padding
12186
12187 if (!m_padding.EqPartial(attr.m_padding, weakTest))
12188 return false;
12189
12190 // Border
12191
12192 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
12193 return false;
12194
12195 // Outline
12196
12197 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
12198 return false;
12199
12200 return true;
12201 }
12202
12203 // Merges the given attributes. If compareWith
12204 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12205 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12206 bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12207 {
12208 if (attr.HasFloatMode())
12209 {
12210 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12211 SetFloatMode(attr.GetFloatMode());
12212 }
12213
12214 if (attr.HasClearMode())
12215 {
12216 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12217 SetClearMode(attr.GetClearMode());
12218 }
12219
12220 if (attr.HasCollapseBorders())
12221 {
12222 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
12223 SetCollapseBorders(attr.GetCollapseBorders());
12224 }
12225
12226 if (attr.HasVerticalAlignment())
12227 {
12228 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12229 SetVerticalAlignment(attr.GetVerticalAlignment());
12230 }
12231
12232 if (attr.HasBoxStyleName())
12233 {
12234 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12235 SetBoxStyleName(attr.GetBoxStyleName());
12236 }
12237
12238 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12239 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12240 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
12241
12242 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
12243 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12244 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
12245
12246 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12247 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
12248
12249 return true;
12250 }
12251
12252 // Remove specified attributes from this object
12253 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12254 {
12255 if (attr.HasFloatMode())
12256 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12257
12258 if (attr.HasClearMode())
12259 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12260
12261 if (attr.HasCollapseBorders())
12262 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12263
12264 if (attr.HasVerticalAlignment())
12265 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12266
12267 if (attr.HasBoxStyleName())
12268 {
12269 SetBoxStyleName(wxEmptyString);
12270 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12271 }
12272
12273 m_margins.RemoveStyle(attr.m_margins);
12274 m_padding.RemoveStyle(attr.m_padding);
12275 m_position.RemoveStyle(attr.m_position);
12276
12277 m_size.RemoveStyle(attr.m_size);
12278 m_minSize.RemoveStyle(attr.m_minSize);
12279 m_maxSize.RemoveStyle(attr.m_maxSize);
12280
12281 m_border.RemoveStyle(attr.m_border);
12282 m_outline.RemoveStyle(attr.m_outline);
12283
12284 return true;
12285 }
12286
12287 // Collects the attributes that are common to a range of content, building up a note of
12288 // which attributes are absent in some objects and which clash in some objects.
12289 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12290 {
12291 if (attr.HasFloatMode())
12292 {
12293 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12294 {
12295 if (HasFloatMode())
12296 {
12297 if (GetFloatMode() != attr.GetFloatMode())
12298 {
12299 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12300 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12301 }
12302 }
12303 else
12304 SetFloatMode(attr.GetFloatMode());
12305 }
12306 }
12307 else
12308 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12309
12310 if (attr.HasClearMode())
12311 {
12312 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12313 {
12314 if (HasClearMode())
12315 {
12316 if (GetClearMode() != attr.GetClearMode())
12317 {
12318 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12319 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12320 }
12321 }
12322 else
12323 SetClearMode(attr.GetClearMode());
12324 }
12325 }
12326 else
12327 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12328
12329 if (attr.HasCollapseBorders())
12330 {
12331 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12332 {
12333 if (HasCollapseBorders())
12334 {
12335 if (GetCollapseBorders() != attr.GetCollapseBorders())
12336 {
12337 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12338 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12339 }
12340 }
12341 else
12342 SetCollapseBorders(attr.GetCollapseBorders());
12343 }
12344 }
12345 else
12346 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12347
12348 if (attr.HasVerticalAlignment())
12349 {
12350 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12351 {
12352 if (HasVerticalAlignment())
12353 {
12354 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12355 {
12356 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12357 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12358 }
12359 }
12360 else
12361 SetVerticalAlignment(attr.GetVerticalAlignment());
12362 }
12363 }
12364 else
12365 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12366
12367 if (attr.HasBoxStyleName())
12368 {
12369 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12370 {
12371 if (HasBoxStyleName())
12372 {
12373 if (GetBoxStyleName() != attr.GetBoxStyleName())
12374 {
12375 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12376 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12377 }
12378 }
12379 else
12380 SetBoxStyleName(attr.GetBoxStyleName());
12381 }
12382 }
12383 else
12384 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12385
12386 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12387 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12388 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12389
12390 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
12391 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12392 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
12393
12394 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12395 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12396 }
12397
12398 bool wxTextBoxAttr::IsDefault() const
12399 {
12400 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
12401 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
12402 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12403 }
12404
12405 // wxRichTextAttr
12406
12407 void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12408 {
12409 wxTextAttr::Copy(attr);
12410
12411 m_textBoxAttr = attr.m_textBoxAttr;
12412 }
12413
12414 bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12415 {
12416 if (!(wxTextAttr::operator==(attr)))
12417 return false;
12418
12419 return (m_textBoxAttr == attr.m_textBoxAttr);
12420 }
12421
12422 // Partial equality test
12423 bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
12424 {
12425 if (!(wxTextAttr::EqPartial(attr, weakTest)))
12426 return false;
12427
12428 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
12429 }
12430
12431 // Merges the given attributes. If compareWith
12432 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12433 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12434 bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12435 {
12436 wxTextAttr::Apply(style, compareWith);
12437
12438 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12439 }
12440
12441 // Remove specified attributes from this object
12442 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12443 {
12444 wxTextAttr::RemoveStyle(*this, attr);
12445
12446 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12447 }
12448
12449 // Collects the attributes that are common to a range of content, building up a note of
12450 // which attributes are absent in some objects and which clash in some objects.
12451 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12452 {
12453 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
12454
12455 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12456 }
12457
12458 // Partial equality test
12459 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
12460 {
12461 if (!weakTest &&
12462 ((!HasStyle() && border.HasStyle()) ||
12463 (!HasColour() && border.HasColour()) ||
12464 (!HasWidth() && border.HasWidth())))
12465 {
12466 return false;
12467 }
12468
12469 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
12470 return false;
12471
12472 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
12473 return false;
12474
12475 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
12476 return false;
12477
12478 return true;
12479 }
12480
12481 // Apply border to 'this', but not if the same as compareWith
12482 bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
12483 {
12484 if (border.HasStyle())
12485 {
12486 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12487 SetStyle(border.GetStyle());
12488 }
12489 if (border.HasColour())
12490 {
12491 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12492 SetColour(border.GetColourLong());
12493 }
12494 if (border.HasWidth())
12495 {
12496 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12497 SetWidth(border.GetWidth());
12498 }
12499
12500 return true;
12501 }
12502
12503 // Remove specified attributes from this object
12504 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
12505 {
12506 if (attr.HasStyle() && HasStyle())
12507 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12508 if (attr.HasColour() && HasColour())
12509 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12510 if (attr.HasWidth() && HasWidth())
12511 m_borderWidth.Reset();
12512
12513 return true;
12514 }
12515
12516 // Collects the attributes that are common to a range of content, building up a note of
12517 // which attributes are absent in some objects and which clash in some objects.
12518 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
12519 {
12520 if (attr.HasStyle())
12521 {
12522 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12523 {
12524 if (HasStyle())
12525 {
12526 if (GetStyle() != attr.GetStyle())
12527 {
12528 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12529 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12530 }
12531 }
12532 else
12533 SetStyle(attr.GetStyle());
12534 }
12535 }
12536 else
12537 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12538
12539 if (attr.HasColour())
12540 {
12541 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12542 {
12543 if (HasColour())
12544 {
12545 if (GetColour() != attr.GetColour())
12546 {
12547 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12548 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12549 }
12550 }
12551 else
12552 SetColour(attr.GetColourLong());
12553 }
12554 }
12555 else
12556 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12557
12558 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12559 }
12560
12561 // Partial equality test
12562 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
12563 {
12564 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12565 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
12566 }
12567
12568 // Apply border to 'this', but not if the same as compareWith
12569 bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
12570 {
12571 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12572 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12573 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12574 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
12575 return true;
12576 }
12577
12578 // Remove specified attributes from this object
12579 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
12580 {
12581 m_left.RemoveStyle(attr.m_left);
12582 m_right.RemoveStyle(attr.m_right);
12583 m_top.RemoveStyle(attr.m_top);
12584 m_bottom.RemoveStyle(attr.m_bottom);
12585 return true;
12586 }
12587
12588 // Collects the attributes that are common to a range of content, building up a note of
12589 // which attributes are absent in some objects and which clash in some objects.
12590 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
12591 {
12592 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12593 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12594 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12595 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12596 }
12597
12598 // Set style of all borders
12599 void wxTextAttrBorders::SetStyle(int style)
12600 {
12601 m_left.SetStyle(style);
12602 m_right.SetStyle(style);
12603 m_top.SetStyle(style);
12604 m_bottom.SetStyle(style);
12605 }
12606
12607 // Set colour of all borders
12608 void wxTextAttrBorders::SetColour(unsigned long colour)
12609 {
12610 m_left.SetColour(colour);
12611 m_right.SetColour(colour);
12612 m_top.SetColour(colour);
12613 m_bottom.SetColour(colour);
12614 }
12615
12616 void wxTextAttrBorders::SetColour(const wxColour& colour)
12617 {
12618 m_left.SetColour(colour);
12619 m_right.SetColour(colour);
12620 m_top.SetColour(colour);
12621 m_bottom.SetColour(colour);
12622 }
12623
12624 // Set width of all borders
12625 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
12626 {
12627 m_left.SetWidth(width);
12628 m_right.SetWidth(width);
12629 m_top.SetWidth(width);
12630 m_bottom.SetWidth(width);
12631 }
12632
12633 // Partial equality test
12634 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
12635 {
12636 if (!weakTest && !IsValid() && dim.IsValid())
12637 return false;
12638
12639 if (dim.IsValid() && IsValid() && !((*this) == dim))
12640 return false;
12641 else
12642 return true;
12643 }
12644
12645 bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12646 {
12647 if (dim.IsValid())
12648 {
12649 if (!(compareWith && dim == (*compareWith)))
12650 (*this) = dim;
12651 }
12652
12653 return true;
12654 }
12655
12656 // Collects the attributes that are common to a range of content, building up a note of
12657 // which attributes are absent in some objects and which clash in some objects.
12658 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12659 {
12660 if (attr.IsValid())
12661 {
12662 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
12663 {
12664 if (IsValid())
12665 {
12666 if (!((*this) == attr))
12667 {
12668 clashingAttr.SetValid(true);
12669 SetValid(false);
12670 }
12671 }
12672 else
12673 (*this) = attr;
12674 }
12675 }
12676 else
12677 absentAttr.SetValid(true);
12678 }
12679
12680 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12681 {
12682 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12683 }
12684
12685 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12686 {
12687 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12688 }
12689
12690 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12691 {
12692 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12693 }
12694
12695 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12696 {
12697 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12698 }
12699
12700 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12701 {
12702 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12703 return ConvertTenthsMMToPixels(dim.GetValue());
12704 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12705 return dim.GetValue();
12706 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12707 {
12708 wxASSERT(m_parentSize != wxDefaultSize);
12709 if (direction == wxHORIZONTAL)
12710 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12711 else
12712 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12713 }
12714 else
12715 {
12716 wxASSERT(false);
12717 return 0;
12718 }
12719 }
12720
12721 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12722 {
12723 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12724 return dim.GetValue();
12725 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12726 return ConvertPixelsToTenthsMM(dim.GetValue());
12727 else
12728 {
12729 wxASSERT(false);
12730 return 0;
12731 }
12732 }
12733
12734 // Partial equality test
12735 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
12736 {
12737 if (!m_left.EqPartial(dims.m_left, weakTest))
12738 return false;
12739
12740 if (!m_right.EqPartial(dims.m_right, weakTest))
12741 return false;
12742
12743 if (!m_top.EqPartial(dims.m_top, weakTest))
12744 return false;
12745
12746 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
12747 return false;
12748
12749 return true;
12750 }
12751
12752 // Apply border to 'this', but not if the same as compareWith
12753 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
12754 {
12755 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12756 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12757 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12758 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12759
12760 return true;
12761 }
12762
12763 // Remove specified attributes from this object
12764 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
12765 {
12766 if (attr.m_left.IsValid())
12767 m_left.Reset();
12768 if (attr.m_right.IsValid())
12769 m_right.Reset();
12770 if (attr.m_top.IsValid())
12771 m_top.Reset();
12772 if (attr.m_bottom.IsValid())
12773 m_bottom.Reset();
12774
12775 return true;
12776 }
12777
12778 // Collects the attributes that are common to a range of content, building up a note of
12779 // which attributes are absent in some objects and which clash in some objects.
12780 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
12781 {
12782 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12783 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12784 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12785 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12786 }
12787
12788 // Partial equality test
12789 bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
12790 {
12791 if (!m_width.EqPartial(size.m_width, weakTest))
12792 return false;
12793
12794 if (!m_height.EqPartial(size.m_height, weakTest))
12795 return false;
12796
12797 return true;
12798 }
12799
12800 // Apply border to 'this', but not if the same as compareWith
12801 bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12802 {
12803 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12804 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12805
12806 return true;
12807 }
12808
12809 // Remove specified attributes from this object
12810 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12811 {
12812 if (attr.m_width.IsValid())
12813 m_width.Reset();
12814 if (attr.m_height.IsValid())
12815 m_height.Reset();
12816
12817 return true;
12818 }
12819
12820 // Collects the attributes that are common to a range of content, building up a note of
12821 // which attributes are absent in some objects and which clash in some objects.
12822 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12823 {
12824 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12825 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12826 }
12827
12828 // Collects the attributes that are common to a range of content, building up a note of
12829 // which attributes are absent in some objects and which clash in some objects.
12830 void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12831 {
12832 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12833 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12834
12835 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12836
12837 // If different font size units are being used, this is a clash.
12838 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
12839 {
12840 currentStyle.SetFontSize(0);
12841 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12842 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12843 }
12844 else
12845 {
12846 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
12847 {
12848 if (currentStyle.HasFontPointSize())
12849 {
12850 if (currentStyle.GetFontSize() != attr.GetFontSize())
12851 {
12852 // Clash of attr - mark as such
12853 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12854 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12855 }
12856 }
12857 else
12858 currentStyle.SetFontSize(attr.GetFontSize());
12859 }
12860 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
12861 {
12862 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12863 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12864 }
12865
12866 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
12867 {
12868 if (currentStyle.HasFontPixelSize())
12869 {
12870 if (currentStyle.GetFontSize() != attr.GetFontSize())
12871 {
12872 // Clash of attr - mark as such
12873 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12874 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12875 }
12876 }
12877 else
12878 currentStyle.SetFontPixelSize(attr.GetFontSize());
12879 }
12880 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
12881 {
12882 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12883 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12884 }
12885 }
12886
12887 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12888 {
12889 if (currentStyle.HasFontItalic())
12890 {
12891 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
12892 {
12893 // Clash of attr - mark as such
12894 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12895 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12896 }
12897 }
12898 else
12899 currentStyle.SetFontStyle(attr.GetFontStyle());
12900 }
12901 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
12902 {
12903 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12904 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12905 }
12906
12907 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12908 {
12909 if (currentStyle.HasFontFamily())
12910 {
12911 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
12912 {
12913 // Clash of attr - mark as such
12914 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12915 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12916 }
12917 }
12918 else
12919 currentStyle.SetFontFamily(attr.GetFontFamily());
12920 }
12921 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
12922 {
12923 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12924 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12925 }
12926
12927 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12928 {
12929 if (currentStyle.HasFontWeight())
12930 {
12931 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
12932 {
12933 // Clash of attr - mark as such
12934 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12935 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12936 }
12937 }
12938 else
12939 currentStyle.SetFontWeight(attr.GetFontWeight());
12940 }
12941 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
12942 {
12943 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12944 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12945 }
12946
12947 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12948 {
12949 if (currentStyle.HasFontFaceName())
12950 {
12951 wxString faceName1(currentStyle.GetFontFaceName());
12952 wxString faceName2(attr.GetFontFaceName());
12953
12954 if (faceName1 != faceName2)
12955 {
12956 // Clash of attr - mark as such
12957 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
12958 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
12959 }
12960 }
12961 else
12962 currentStyle.SetFontFaceName(attr.GetFontFaceName());
12963 }
12964 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
12965 {
12966 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
12967 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
12968 }
12969
12970 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
12971 {
12972 if (currentStyle.HasFontUnderlined())
12973 {
12974 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
12975 {
12976 // Clash of attr - mark as such
12977 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12978 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12979 }
12980 }
12981 else
12982 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
12983 }
12984 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
12985 {
12986 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12987 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
12988 }
12989
12990 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
12991 {
12992 if (currentStyle.HasFontStrikethrough())
12993 {
12994 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
12995 {
12996 // Clash of attr - mark as such
12997 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12998 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
12999 }
13000 }
13001 else
13002 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13003 }
13004 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13005 {
13006 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13007 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13008 }
13009
13010 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13011 {
13012 if (currentStyle.HasTextColour())
13013 {
13014 if (currentStyle.GetTextColour() != attr.GetTextColour())
13015 {
13016 // Clash of attr - mark as such
13017 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13018 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13019 }
13020 }
13021 else
13022 currentStyle.SetTextColour(attr.GetTextColour());
13023 }
13024 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13025 {
13026 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13027 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13028 }
13029
13030 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13031 {
13032 if (currentStyle.HasBackgroundColour())
13033 {
13034 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13035 {
13036 // Clash of attr - mark as such
13037 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13038 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13039 }
13040 }
13041 else
13042 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13043 }
13044 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13045 {
13046 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13047 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13048 }
13049
13050 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13051 {
13052 if (currentStyle.HasAlignment())
13053 {
13054 if (currentStyle.GetAlignment() != attr.GetAlignment())
13055 {
13056 // Clash of attr - mark as such
13057 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13058 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13059 }
13060 }
13061 else
13062 currentStyle.SetAlignment(attr.GetAlignment());
13063 }
13064 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13065 {
13066 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13067 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13068 }
13069
13070 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13071 {
13072 if (currentStyle.HasTabs())
13073 {
13074 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13075 {
13076 // Clash of attr - mark as such
13077 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13078 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13079 }
13080 }
13081 else
13082 currentStyle.SetTabs(attr.GetTabs());
13083 }
13084 else if (!attr.HasTabs() && currentStyle.HasTabs())
13085 {
13086 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13087 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13088 }
13089
13090 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13091 {
13092 if (currentStyle.HasLeftIndent())
13093 {
13094 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13095 {
13096 // Clash of attr - mark as such
13097 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13098 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13099 }
13100 }
13101 else
13102 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13103 }
13104 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13105 {
13106 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13107 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13108 }
13109
13110 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13111 {
13112 if (currentStyle.HasRightIndent())
13113 {
13114 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13115 {
13116 // Clash of attr - mark as such
13117 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13118 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13119 }
13120 }
13121 else
13122 currentStyle.SetRightIndent(attr.GetRightIndent());
13123 }
13124 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13125 {
13126 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13127 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13128 }
13129
13130 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13131 {
13132 if (currentStyle.HasParagraphSpacingAfter())
13133 {
13134 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13135 {
13136 // Clash of attr - mark as such
13137 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13138 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13139 }
13140 }
13141 else
13142 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13143 }
13144 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13145 {
13146 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13147 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13148 }
13149
13150 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13151 {
13152 if (currentStyle.HasParagraphSpacingBefore())
13153 {
13154 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13155 {
13156 // Clash of attr - mark as such
13157 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13158 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13159 }
13160 }
13161 else
13162 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13163 }
13164 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13165 {
13166 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13167 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13168 }
13169
13170 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13171 {
13172 if (currentStyle.HasLineSpacing())
13173 {
13174 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13175 {
13176 // Clash of attr - mark as such
13177 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13178 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13179 }
13180 }
13181 else
13182 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13183 }
13184 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13185 {
13186 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13187 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13188 }
13189
13190 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13191 {
13192 if (currentStyle.HasCharacterStyleName())
13193 {
13194 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13195 {
13196 // Clash of attr - mark as such
13197 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13198 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13199 }
13200 }
13201 else
13202 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13203 }
13204 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13205 {
13206 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13207 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13208 }
13209
13210 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13211 {
13212 if (currentStyle.HasParagraphStyleName())
13213 {
13214 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13215 {
13216 // Clash of attr - mark as such
13217 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13218 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13219 }
13220 }
13221 else
13222 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13223 }
13224 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13225 {
13226 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13227 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13228 }
13229
13230 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13231 {
13232 if (currentStyle.HasListStyleName())
13233 {
13234 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13235 {
13236 // Clash of attr - mark as such
13237 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13238 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13239 }
13240 }
13241 else
13242 currentStyle.SetListStyleName(attr.GetListStyleName());
13243 }
13244 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13245 {
13246 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13247 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13248 }
13249
13250 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13251 {
13252 if (currentStyle.HasBulletStyle())
13253 {
13254 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13255 {
13256 // Clash of attr - mark as such
13257 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13258 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13259 }
13260 }
13261 else
13262 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13263 }
13264 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13265 {
13266 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13267 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13268 }
13269
13270 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13271 {
13272 if (currentStyle.HasBulletNumber())
13273 {
13274 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13275 {
13276 // Clash of attr - mark as such
13277 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13278 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13279 }
13280 }
13281 else
13282 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13283 }
13284 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13285 {
13286 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13287 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13288 }
13289
13290 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13291 {
13292 if (currentStyle.HasBulletText())
13293 {
13294 if (currentStyle.GetBulletText() != attr.GetBulletText())
13295 {
13296 // Clash of attr - mark as such
13297 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13298 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13299 }
13300 }
13301 else
13302 {
13303 currentStyle.SetBulletText(attr.GetBulletText());
13304 currentStyle.SetBulletFont(attr.GetBulletFont());
13305 }
13306 }
13307 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13308 {
13309 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13310 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13311 }
13312
13313 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13314 {
13315 if (currentStyle.HasBulletName())
13316 {
13317 if (currentStyle.GetBulletName() != attr.GetBulletName())
13318 {
13319 // Clash of attr - mark as such
13320 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13321 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13322 }
13323 }
13324 else
13325 {
13326 currentStyle.SetBulletName(attr.GetBulletName());
13327 }
13328 }
13329 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13330 {
13331 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13332 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13333 }
13334
13335 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13336 {
13337 if (currentStyle.HasURL())
13338 {
13339 if (currentStyle.GetURL() != attr.GetURL())
13340 {
13341 // Clash of attr - mark as such
13342 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13343 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13344 }
13345 }
13346 else
13347 {
13348 currentStyle.SetURL(attr.GetURL());
13349 }
13350 }
13351 else if (!attr.HasURL() && currentStyle.HasURL())
13352 {
13353 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13354 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13355 }
13356
13357 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13358 {
13359 if (currentStyle.HasTextEffects())
13360 {
13361 // We need to find the bits in the new attr that are different:
13362 // just look at those bits that are specified by the new attr.
13363
13364 // We need to remove the bits and flags that are not common between current attr
13365 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13366 // previous styles.
13367
13368 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13369 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13370
13371 if (currentRelevantTextEffects != newRelevantTextEffects)
13372 {
13373 // Find the text effects that were different, using XOR
13374 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13375
13376 // Clash of attr - mark as such
13377 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13378 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13379 }
13380 }
13381 else
13382 {
13383 currentStyle.SetTextEffects(attr.GetTextEffects());
13384 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13385 }
13386
13387 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13388 // that we've looked at so far
13389 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13390 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13391
13392 if (currentStyle.GetTextEffectFlags() == 0)
13393 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13394 }
13395 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13396 {
13397 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13398 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13399 }
13400
13401 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13402 {
13403 if (currentStyle.HasOutlineLevel())
13404 {
13405 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13406 {
13407 // Clash of attr - mark as such
13408 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13409 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13410 }
13411 }
13412 else
13413 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13414 }
13415 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13416 {
13417 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13418 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13419 }
13420 }
13421
13422 WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13423
13424 // JACS 2013-01-27
13425 WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13426
13427 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13428
13429 bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13430 {
13431 if (m_properties.GetCount() != props.GetCount())
13432 return false;
13433
13434 size_t i;
13435 for (i = 0; i < m_properties.GetCount(); i++)
13436 {
13437 const wxVariant& var1 = m_properties[i];
13438 int idx = props.Find(var1.GetName());
13439 if (idx == -1)
13440 return false;
13441 const wxVariant& var2 = props.m_properties[idx];
13442 if (!(var1 == var2))
13443 return false;
13444 }
13445
13446 return true;
13447 }
13448
13449 wxArrayString wxRichTextProperties::GetPropertyNames() const
13450 {
13451 wxArrayString arr;
13452 size_t i;
13453 for (i = 0; i < m_properties.GetCount(); i++)
13454 {
13455 arr.Add(m_properties[i].GetName());
13456 }
13457 return arr;
13458 }
13459
13460 int wxRichTextProperties::Find(const wxString& name) const
13461 {
13462 size_t i;
13463 for (i = 0; i < m_properties.GetCount(); i++)
13464 {
13465 if (m_properties[i].GetName() == name)
13466 return (int) i;
13467 }
13468 return -1;
13469 }
13470
13471 bool wxRichTextProperties::Remove(const wxString& name)
13472 {
13473 int idx = Find(name);
13474 if (idx != -1)
13475 {
13476 m_properties.RemoveAt(idx);
13477 return true;
13478 }
13479 else
13480 return false;
13481 }
13482
13483 wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13484 {
13485 int idx = Find(name);
13486 if (idx == wxNOT_FOUND)
13487 SetProperty(name, wxString());
13488 idx = Find(name);
13489 if (idx != wxNOT_FOUND)
13490 {
13491 return & (*this)[idx];
13492 }
13493 else
13494 return NULL;
13495 }
13496
13497 const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13498 {
13499 static const wxVariant nullVariant;
13500 int idx = Find(name);
13501 if (idx != -1)
13502 return m_properties[idx];
13503 else
13504 return nullVariant;
13505 }
13506
13507 wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13508 {
13509 return GetProperty(name).GetString();
13510 }
13511
13512 long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13513 {
13514 return GetProperty(name).GetLong();
13515 }
13516
13517 bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13518 {
13519 return GetProperty(name).GetBool();
13520 }
13521
13522 double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13523 {
13524 return GetProperty(name).GetDouble();
13525 }
13526
13527 void wxRichTextProperties::SetProperty(const wxVariant& variant)
13528 {
13529 wxASSERT(!variant.GetName().IsEmpty());
13530
13531 int idx = Find(variant.GetName());
13532
13533 if (idx == -1)
13534 m_properties.Add(variant);
13535 else
13536 m_properties[idx] = variant;
13537 }
13538
13539 void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13540 {
13541 int idx = Find(name);
13542 wxVariant var(variant);
13543 var.SetName(name);
13544
13545 if (idx == -1)
13546 m_properties.Add(var);
13547 else
13548 m_properties[idx] = var;
13549 }
13550
13551 void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13552 {
13553 SetProperty(name, wxVariant(value, name));
13554 }
13555
13556 void wxRichTextProperties::SetProperty(const wxString& name, long value)
13557 {
13558 SetProperty(name, wxVariant(value, name));
13559 }
13560
13561 void wxRichTextProperties::SetProperty(const wxString& name, double value)
13562 {
13563 SetProperty(name, wxVariant(value, name));
13564 }
13565
13566 void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13567 {
13568 SetProperty(name, wxVariant(value, name));
13569 }
13570
13571 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13572 {
13573 size_t i;
13574 for (i = 0; i < properties.GetCount(); i++)
13575 {
13576 wxString name = properties.GetProperties()[i].GetName();
13577 if (HasProperty(name))
13578 Remove(name);
13579 }
13580 }
13581
13582 void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13583 {
13584 size_t i;
13585 for (i = 0; i < properties.GetCount(); i++)
13586 {
13587 SetProperty(properties.GetProperties()[i]);
13588 }
13589 }
13590
13591 wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13592 {
13593 if (m_address.GetCount() == 0)
13594 return topLevelContainer;
13595
13596 wxRichTextCompositeObject* p = topLevelContainer;
13597 size_t i = 0;
13598 while (p && i < m_address.GetCount())
13599 {
13600 int pos = m_address[i];
13601 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13602 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13603 return NULL;
13604
13605 wxRichTextObject* p1 = p->GetChild(pos);
13606 if (i == (m_address.GetCount()-1))
13607 return p1;
13608
13609 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13610 i ++;
13611 }
13612 return NULL;
13613 }
13614
13615 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13616 {
13617 m_address.Clear();
13618
13619 if (topLevelContainer == obj)
13620 return true;
13621
13622 wxRichTextObject* o = obj;
13623 while (o)
13624 {
13625 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13626 if (!p)
13627 return false;
13628
13629 int pos = p->GetChildren().IndexOf(o);
13630 if (pos == -1)
13631 return false;
13632
13633 m_address.Insert(pos, 0);
13634
13635 if (p == topLevelContainer)
13636 return true;
13637
13638 o = p;
13639 }
13640 return false;
13641 }
13642
13643 // Equality test
13644 bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13645 {
13646 if (m_container != sel.m_container)
13647 return false;
13648 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13649 return false;
13650 size_t i;
13651 for (i = 0; i < m_ranges.GetCount(); i++)
13652 if (!(m_ranges[i] == sel.m_ranges[i]))
13653 return false;
13654 return true;
13655 }
13656
13657 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13658 // or none at the level of the object's container.
13659 wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13660 {
13661 if (IsValid())
13662 {
13663 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13664
13665 if (container == m_container)
13666 return m_ranges;
13667
13668 container = obj->GetContainer();
13669 while (container)
13670 {
13671 if (container->GetParent())
13672 {
13673 // If we found that our object's container is within the range of
13674 // a selection higher up, then assume the whole original object
13675 // is also selected.
13676 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13677 if (parentContainer == m_container)
13678 {
13679 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13680 {
13681 wxRichTextRangeArray ranges;
13682 ranges.Add(obj->GetRange());
13683 return ranges;
13684 }
13685 }
13686
13687 container = parentContainer;
13688 }
13689 else
13690 {
13691 container = NULL;
13692 break;
13693 }
13694 }
13695 }
13696 return wxRichTextRangeArray();
13697 }
13698
13699 // Is the given position within the selection?
13700 bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13701 {
13702 if (!IsValid())
13703 return false;
13704 else
13705 {
13706 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13707 return WithinSelection(pos, selectionRanges);
13708 }
13709 }
13710
13711 // Is the given position within the selection range?
13712 bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13713 {
13714 size_t i;
13715 for (i = 0; i < ranges.GetCount(); i++)
13716 {
13717 const wxRichTextRange& range = ranges[i];
13718 if (pos >= range.GetStart() && pos <= range.GetEnd())
13719 return true;
13720 }
13721 return false;
13722 }
13723
13724 // Is the given range completely within the selection range?
13725 bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13726 {
13727 size_t i;
13728 for (i = 0; i < ranges.GetCount(); i++)
13729 {
13730 const wxRichTextRange& eachRange = ranges[i];
13731 if (range.IsWithin(eachRange))
13732 return true;
13733 }
13734 return false;
13735 }
13736
13737 IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13738 IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13739
13740 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
13741 {
13742 Init();
13743 m_buffer = buffer;
13744 if (m_buffer && m_buffer->GetRichTextCtrl())
13745 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13746 }
13747
13748 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13749 {
13750 if (!GetVirtualAttributesEnabled())
13751 return false;
13752
13753 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13754 while (node)
13755 {
13756 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13757 if (handler->HasVirtualAttributes(obj))
13758 return true;
13759
13760 node = node->GetNext();
13761 }
13762 return false;
13763 }
13764
13765 wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13766 {
13767 wxRichTextAttr attr;
13768 if (!GetVirtualAttributesEnabled())
13769 return attr;
13770
13771 // We apply all handlers, so we can may combine several different attributes
13772 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13773 while (node)
13774 {
13775 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13776 if (handler->HasVirtualAttributes(obj))
13777 {
13778 bool success = handler->GetVirtualAttributes(attr, obj);
13779 wxASSERT(success);
13780 wxUnusedVar(success);
13781 }
13782
13783 node = node->GetNext();
13784 }
13785 return attr;
13786 }
13787
13788 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13789 {
13790 if (!GetVirtualAttributesEnabled())
13791 return false;
13792
13793 if (HasVirtualAttributes(obj))
13794 {
13795 wxRichTextAttr a(GetVirtualAttributes(obj));
13796 attr.Apply(a);
13797 return true;
13798 }
13799 else
13800 return false;
13801 }
13802
13803 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
13804 {
13805 if (!GetVirtualAttributesEnabled())
13806 return 0;
13807
13808 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13809 while (node)
13810 {
13811 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13812 int count = handler->GetVirtualSubobjectAttributesCount(obj);
13813 if (count > 0)
13814 return count;
13815
13816 node = node->GetNext();
13817 }
13818 return 0;
13819 }
13820
13821 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
13822 {
13823 if (!GetVirtualAttributesEnabled())
13824 return 0;
13825
13826 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13827 while (node)
13828 {
13829 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13830 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
13831 return positions.GetCount();
13832
13833 node = node->GetNext();
13834 }
13835 return 0;
13836 }
13837
13838 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
13839 {
13840 if (!GetVirtualAttributesEnabled())
13841 return false;
13842
13843 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13844 while (node)
13845 {
13846 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13847 if (handler->HasVirtualText(obj))
13848 return true;
13849
13850 node = node->GetNext();
13851 }
13852 return false;
13853 }
13854
13855 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
13856 {
13857 if (!GetVirtualAttributesEnabled())
13858 return false;
13859
13860 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13861 while (node)
13862 {
13863 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13864 if (handler->GetVirtualText(obj, text))
13865 return true;
13866
13867 node = node->GetNext();
13868 }
13869 return false;
13870 }
13871
13872 /// Adds a handler to the end
13873 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13874 {
13875 sm_drawingHandlers.Append(handler);
13876 }
13877
13878 /// Inserts a handler at the front
13879 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13880 {
13881 sm_drawingHandlers.Insert( handler );
13882 }
13883
13884 /// Removes a handler
13885 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13886 {
13887 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13888 if (handler)
13889 {
13890 sm_drawingHandlers.DeleteObject(handler);
13891 delete handler;
13892 return true;
13893 }
13894 else
13895 return false;
13896 }
13897
13898 wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13899 {
13900 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13901 while (node)
13902 {
13903 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13904 if (handler->GetName().Lower() == name.Lower()) return handler;
13905
13906 node = node->GetNext();
13907 }
13908 return NULL;
13909 }
13910
13911 void wxRichTextBuffer::CleanUpDrawingHandlers()
13912 {
13913 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13914 while (node)
13915 {
13916 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13917 wxList::compatibility_iterator next = node->GetNext();
13918 delete handler;
13919 node = next;
13920 }
13921
13922 sm_drawingHandlers.Clear();
13923 }
13924
13925 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13926 {
13927 sm_fieldTypes[fieldType->GetName()] = fieldType;
13928 }
13929
13930 bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13931 {
13932 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13933 if (it == sm_fieldTypes.end())
13934 return false;
13935 else
13936 {
13937 wxRichTextFieldType* fieldType = it->second;
13938 sm_fieldTypes.erase(it);
13939 delete fieldType;
13940 return true;
13941 }
13942 }
13943
13944 wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13945 {
13946 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13947 if (it == sm_fieldTypes.end())
13948 return NULL;
13949 else
13950 return it->second;
13951 }
13952
13953 void wxRichTextBuffer::CleanUpFieldTypes()
13954 {
13955 wxRichTextFieldTypeHashMap::iterator it;
13956 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
13957 {
13958 wxRichTextFieldType* fieldType = it->second;
13959 delete fieldType;
13960 }
13961
13962 sm_fieldTypes.clear();
13963 }
13964
13965 #endif
13966 // wxUSE_RICHTEXT