Corrected loading of images: we need the parent size to compute initial object size
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT
20
21 #include "wx/richtext/richtextbuffer.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/dc.h"
25 #include "wx/intl.h"
26 #include "wx/log.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
29 #endif
30
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
40
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
46
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
49
50 WX_DEFINE_LIST(wxRichTextObjectList)
51 WX_DEFINE_LIST(wxRichTextLineList)
52
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58
59 const wxChar wxRichTextLineBreakChar = (wxChar) 29;
60
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
63 {
64 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
65 {
66 startY = sY;
67 endY = eY;
68 width = w;
69 anchor = obj;
70 }
71
72 int startY, endY;
73 int width;
74 wxRichTextObject* anchor;
75 };
76
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
78
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
80 {
81 return r1->startY - r2->startY;
82 }
83
84 class wxRichTextFloatCollector
85 {
86 public:
87 wxRichTextFloatCollector(const wxRect& availableRect);
88 ~wxRichTextFloatCollector();
89
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph* para);
92 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
93
94 // Return the last paragraph we collected
95 wxRichTextParagraph* LastParagraph();
96
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect GetAvailableRect(int startY, int endY);
100
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction, int start, int height) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
104
105 // Find the last y position
106 int GetLastRectBottom();
107
108 // Draw the floats inside a rect
109 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
110
111 // HitTest the floats
112 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
113
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
116
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
119
120 // Delete a float
121 bool DeleteFloat(wxRichTextObject* obj);
122
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject* obj);
125
126 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
127
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
129
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
131
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
133
134 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
135
136 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
137
138 private:
139 wxRichTextFloatRectMapArray m_left;
140 wxRichTextFloatRectMapArray m_right;
141 //int m_width;
142 wxRect m_availableRect;
143 wxRichTextParagraph* m_para;
144 };
145
146 // Delete a float
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
148 {
149 size_t i;
150 for (i = 0; i < m_left.GetCount(); i++)
151 {
152 if (m_left[i]->anchor == obj)
153 {
154 m_left.RemoveAt(i);
155 return true;
156 }
157 }
158 for (i = 0; i < m_right.GetCount(); i++)
159 {
160 if (m_right[i]->anchor == obj)
161 {
162 m_right.RemoveAt(i);
163 return true;
164 }
165 }
166 return false;
167 }
168
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
171 {
172 size_t i;
173 for (i = 0; i < m_left.GetCount(); i++)
174 {
175 if (m_left[i]->anchor == obj)
176 {
177 return true;
178 }
179 }
180 for (i = 0; i < m_right.GetCount(); i++)
181 {
182 if (m_right[i]->anchor == obj)
183 {
184 return true;
185 }
186 }
187 return false;
188 }
189
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
192 {
193 size_t i;
194 for (i = 0; i < m_left.GetCount(); i++)
195 objects.Append(m_left[i]->anchor);
196 for (i = 0; i < m_right.GetCount(); i++)
197 objects.Append(m_right[i]->anchor);
198 return true;
199 }
200
201
202 /*
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
207 */
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
209 {
210 int end = array.GetCount() - 1;
211 int start = 0;
212 int ret = 0;
213
214 wxASSERT(end >= 0);
215
216 while (true)
217 {
218 if (start > end)
219 {
220 break;
221 }
222
223 int mid = (start + end) / 2;
224 if (array[mid]->startY <= point && array[mid]->endY >= point)
225 return mid;
226 else if (array[mid]->startY > point)
227 {
228 end = mid - 1;
229 ret = mid;
230 }
231 else if (array[mid]->endY < point)
232 {
233 start = mid + 1;
234 ret = start;
235 }
236 }
237
238 return ret;
239 }
240
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
242 {
243 int ret = 0;
244 int len = array.GetCount();
245
246 wxASSERT(index >= 0 && index < len);
247
248 if (array[index]->startY < startY && array[index]->endY > startY)
249 ret = ret < array[index]->width ? array[index]->width : ret;
250 while (index < len && array[index]->startY <= endY)
251 {
252 ret = ret < array[index]->width ? array[index]->width : ret;
253 index++;
254 }
255
256 return ret;
257 }
258
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
260 {
261 m_availableRect = rect;
262 m_para = NULL;
263 }
264
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
266 {
267 int len = array.GetCount();
268 for (int i = 0; i < len; i++)
269 delete array[i];
270 }
271
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 {
274 FreeFloatRectMapArray(m_left);
275 FreeFloatRectMapArray(m_right);
276 }
277
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
279 {
280 if (array.GetCount() == 0)
281 return start;
282
283 int i = SearchAdjacentRect(array, start);
284 int last = start;
285 while (i < (int) array.GetCount())
286 {
287 if (array[i]->startY - last >= height)
288 return last + 1;
289 last = array[i]->endY;
290 i++;
291 }
292
293 return last + 1;
294 }
295
296 int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
297 {
298 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
299 return GetFitPosition(m_left, start, height);
300 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
301 return GetFitPosition(m_right, start, height);
302 else
303 {
304 wxASSERT("Never should be here");
305 return start;
306 }
307 }
308
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
312 {
313 int direction = floating->GetFloatDirection();
314
315 wxPoint pos = floating->GetPosition();
316 wxSize size = floating->GetCachedSize();
317 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
318 switch (direction)
319 {
320 case wxTEXT_BOX_ATTR_FLOAT_NONE:
321 delete map;
322 break;
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
326 m_left.Add(map);
327 break;
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
330 m_right.Add(map);
331 break;
332 default:
333 delete map;
334 wxASSERT("Unrecognised float attribute.");
335 }
336
337 m_para = para;
338 }
339
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
341 {
342 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
343 while (node)
344 {
345 wxRichTextObject* floating = node->GetData();
346
347 if (floating->IsFloating())
348 {
349 CollectFloat(para, floating);
350 }
351
352 node = node->GetNext();
353 }
354
355 m_para = para;
356 }
357
358 wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
359 {
360 return m_para;
361 }
362
363 wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
364 {
365 int widthLeft = 0, widthRight = 0;
366 if (m_left.GetCount() != 0)
367 {
368 int i = SearchAdjacentRect(m_left, startY);
369 if (i < (int) m_left.GetCount())
370 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
371 }
372 if (m_right.GetCount() != 0)
373 {
374 int j = SearchAdjacentRect(m_right, startY);
375 if (j < (int) m_right.GetCount())
376 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
377 }
378
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
383 }
384
385 int wxRichTextFloatCollector::GetLastRectBottom()
386 {
387 int ret = 0;
388 int len = m_left.GetCount();
389 if (len) {
390 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
391 }
392 len = m_right.GetCount();
393 if (len) {
394 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
395 }
396
397 return ret;
398 }
399
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
401 {
402 int start = rect.y;
403 int end = rect.y + rect.height;
404 int i, j;
405 i = SearchAdjacentRect(array, start);
406 if (i < 0 || i >= (int) array.GetCount())
407 return;
408 j = SearchAdjacentRect(array, end);
409 if (j < 0 || j >= (int) array.GetCount())
410 j = array.GetCount() - 1;
411 while (i <= j)
412 {
413 wxRichTextObject* obj = array[i]->anchor;
414 wxRichTextRange r = obj->GetRange();
415 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
416 i++;
417 }
418 }
419
420 void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
421 {
422 if (m_left.GetCount() > 0)
423 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
424 if (m_right.GetCount() > 0)
425 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
426 }
427
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
429 {
430 int i;
431 if (array.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE;
433 i = SearchAdjacentRect(array, pt.y);
434 if (i < 0 || i >= (int) array.GetCount())
435 return wxRICHTEXT_HITTEST_NONE;
436 if (!array[i]->anchor->IsShown())
437 return wxRICHTEXT_HITTEST_NONE;
438
439 wxPoint point = array[i]->anchor->GetPosition();
440 wxSize size = array[i]->anchor->GetCachedSize();
441 if (point.x <= pt.x && point.x + size.x >= pt.x
442 && point.y <= pt.y && point.y + size.y >= pt.y)
443 {
444 textPosition = array[i]->anchor->GetRange().GetStart();
445 * obj = array[i]->anchor;
446 if (pt.x > (pt.x + pt.x + size.x) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE;
448 else
449 return wxRICHTEXT_HITTEST_AFTER;
450 }
451
452 return wxRICHTEXT_HITTEST_NONE;
453 }
454
455 int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
456 {
457 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
458 if (ret == wxRICHTEXT_HITTEST_NONE)
459 {
460 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
461 }
462 return ret;
463 }
464
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
467 {
468 dc.SetFont(font);
469 }
470
471 inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
472 {
473 const wxPen& pen1 = dc.GetPen();
474 if (pen1.IsOk() && pen.IsOk())
475 {
476 if (pen1.GetWidth() == pen.GetWidth() &&
477 pen1.GetStyle() == pen.GetStyle() &&
478 pen1.GetColour() == pen.GetColour())
479 return;
480 }
481 dc.SetPen(pen);
482 }
483
484 inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
485 {
486 const wxBrush& brush1 = dc.GetBrush();
487 if (brush1.IsOk() && brush.IsOk())
488 {
489 if (brush1.GetStyle() == brush.GetStyle() &&
490 brush1.GetColour() == brush.GetColour())
491 return;
492 }
493 dc.SetBrush(brush);
494 }
495
496 /*!
497 * wxRichTextObject
498 * This is the base for drawable objects.
499 */
500
501 IMPLEMENT_CLASS(wxRichTextObject, wxObject)
502
503 wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
504 {
505 m_refCount = 1;
506 m_parent = parent;
507 m_descent = 0;
508 m_show = true;
509 }
510
511 wxRichTextObject::~wxRichTextObject()
512 {
513 }
514
515 void wxRichTextObject::Dereference()
516 {
517 m_refCount --;
518 if (m_refCount <= 0)
519 delete this;
520 }
521
522 /// Copy
523 void wxRichTextObject::Copy(const wxRichTextObject& obj)
524 {
525 m_size = obj.m_size;
526 m_maxSize = obj.m_maxSize;
527 m_minSize = obj.m_minSize;
528 m_pos = obj.m_pos;
529 m_range = obj.m_range;
530 m_ownRange = obj.m_ownRange;
531 m_attributes = obj.m_attributes;
532 m_properties = obj.m_properties;
533 m_descent = obj.m_descent;
534 m_show = obj.m_show;
535 }
536
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
539 {
540 const wxRichTextObject* p = this;
541 while (p)
542 {
543 if (p->IsTopLevel())
544 {
545 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
546 }
547 p = p->GetParent();
548 }
549 return NULL;
550 }
551
552 void wxRichTextObject::SetMargins(int margin)
553 {
554 SetMargins(margin, margin, margin, margin);
555 }
556
557 void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
558 {
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
563 }
564
565 int wxRichTextObject::GetLeftMargin() const
566 {
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
568 }
569
570 int wxRichTextObject::GetRightMargin() const
571 {
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
573 }
574
575 int wxRichTextObject::GetTopMargin() const
576 {
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
578 }
579
580 int wxRichTextObject::GetBottomMargin() const
581 {
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
583 }
584
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
588 {
589 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590 marginRect = outerRect;
591 wxRichTextAttr attr(GetAttributes());
592 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
594 return contentRect;
595 }
596
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
599 {
600 if (invalidRange != wxRICHTEXT_NONE)
601 {
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
606 SetCachedSize(wxDefaultSize);
607 SetMaxSize(wxDefaultSize);
608 SetMinSize(wxDefaultSize);
609 }
610 }
611
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
614 {
615 // Unscale
616 double scale = 1.0;
617 if (GetBuffer())
618 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
620
621 return p;
622 }
623
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
626 {
627 // There are ppi pixels in 254.1 "1/10 mm"
628
629 double pixels = ((double) units * (double)ppi) / 254.1;
630 if (scale != 1.0)
631 pixels /= scale;
632
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels == 0 && units > 0)
635 pixels = 1;
636
637 return (int) pixels;
638 }
639
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
642 {
643 int p = pixels;
644 double scale = 1.0;
645 if (GetBuffer())
646 scale = GetBuffer()->GetScale();
647
648 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
649 }
650
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
652 {
653 // There are ppi pixels in 254.1 "1/10 mm"
654
655 double p = double(pixels);
656
657 if (scale != 1.0)
658 p *= scale;
659
660 int units = int( p * 254.1 / (double) ppi );
661 return units;
662 }
663
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
667 {
668 // Assume boxRect is the area around the content
669 wxRect marginRect = boxRect;
670 wxRect contentRect, borderRect, paddingRect, outlineRect;
671
672 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
673
674 // Margin is transparent. Draw background from margin.
675 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
676 {
677 wxColour colour;
678 if (flags & wxRICHTEXT_DRAW_SELECTED)
679 {
680 // TODO: get selection colour from control?
681 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
682 }
683 else
684 colour = attr.GetBackgroundColour();
685
686 wxPen pen(colour);
687 wxBrush brush(colour);
688
689 dc.SetPen(pen);
690 dc.SetBrush(brush);
691 dc.DrawRectangle(borderRect);
692 }
693
694 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
695 {
696 wxRichTextAttr editBorderAttr = attr;
697 // TODO: make guideline colour configurable
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
701
702 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
703 }
704
705 if (attr.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
707
708 if (attr.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
710
711 return true;
712 }
713
714 // Draw a border
715 bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
716 {
717 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
718 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
719
720 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
721 {
722 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723 wxColour col(attr.GetLeft().GetColour());
724
725 // If pen width is > 1, resorts to a solid rectangle.
726 if (borderLeft == 1)
727 {
728 int penStyle = wxSOLID;
729 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
730 penStyle = wxDOT;
731 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732 penStyle = wxLONG_DASH;
733 wxPen pen(col, 1, penStyle);
734 dc.SetPen(pen);
735 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
736
737 }
738 else if (borderLeft > 1)
739 {
740 wxPen pen(col);
741 wxBrush brush(col);
742 dc.SetPen(pen);
743 dc.SetBrush(brush);
744 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
745 }
746 }
747
748 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
749 {
750 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
751
752 wxColour col(attr.GetRight().GetColour());
753
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight == 1)
756 {
757 int penStyle = wxSOLID;
758 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
759 penStyle = wxDOT;
760 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761 penStyle = wxLONG_DASH;
762 wxPen pen(col, 1, penStyle);
763 dc.SetPen(pen);
764 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
765
766 }
767 else if (borderRight > 1)
768 {
769 wxPen pen(col);
770 wxBrush brush(col);
771 dc.SetPen(pen);
772 dc.SetBrush(brush);
773 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
774 }
775 }
776
777 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
778 {
779 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
780
781 wxColour col(attr.GetTop().GetColour());
782
783 // If pen width is > 1, resorts to a solid rectangle.
784 if (borderTop == 1)
785 {
786 int penStyle = wxSOLID;
787 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
788 penStyle = wxDOT;
789 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790 penStyle = wxLONG_DASH;
791 wxPen pen(col, 1, penStyle);
792 dc.SetPen(pen);
793 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
794
795 }
796 else if (borderTop > 1)
797 {
798 wxPen pen(col);
799 wxBrush brush(col);
800 dc.SetPen(pen);
801 dc.SetBrush(brush);
802 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
803 }
804 }
805
806 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
807 {
808 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
809 wxColour col(attr.GetBottom().GetColour());
810
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom == 1)
813 {
814 int penStyle = wxSOLID;
815 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
816 penStyle = wxDOT;
817 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818 penStyle = wxLONG_DASH;
819 wxPen pen(col, 1, penStyle);
820 dc.SetPen(pen);
821 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
822
823 }
824 else if (borderBottom > 1)
825 {
826 wxPen pen(col);
827 wxBrush brush(col);
828 dc.SetPen(pen);
829 dc.SetBrush(brush);
830 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
831 }
832 }
833
834 return true;
835 }
836
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
840 // is available.
841 //
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843
844 bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
845 {
846 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
850
851 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
852
853 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
861
862 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870
871 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
879
880 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888
889 int leftTotal = marginLeft + borderLeft + paddingLeft;
890 int rightTotal = marginRight + borderRight + paddingRight;
891 int topTotal = marginTop + borderTop + paddingTop;
892 int bottomTotal = marginBottom + borderBottom + paddingBottom;
893
894 if (marginRect != wxRect())
895 {
896 contentRect.x = marginRect.x + leftTotal;
897 contentRect.y = marginRect.y + topTotal;
898 contentRect.width = marginRect.width - (leftTotal + rightTotal);
899 contentRect.height = marginRect.height - (topTotal + bottomTotal);
900 }
901 else
902 {
903 marginRect.x = contentRect.x - leftTotal;
904 marginRect.y = contentRect.y - topTotal;
905 marginRect.width = contentRect.width + (leftTotal + rightTotal);
906 marginRect.height = contentRect.height + (topTotal + bottomTotal);
907 }
908
909 borderRect.x = marginRect.x + marginLeft;
910 borderRect.y = marginRect.y + marginTop;
911 borderRect.width = marginRect.width - (marginLeft + marginRight);
912 borderRect.height = marginRect.height - (marginTop + marginBottom);
913
914 paddingRect.x = marginRect.x + marginLeft + borderLeft;
915 paddingRect.y = marginRect.y + marginTop + borderTop;
916 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
918
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect.x = marginRect.x - outlineLeft;
921 outlineRect.y = marginRect.y - outlineTop;
922 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
924
925 return true;
926 }
927
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930 int& topMargin, int& bottomMargin)
931 {
932 // Assume boxRect is the area around the content
933 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934 marginRect = wxRect(0, 0, 1000, 1000);
935
936 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
937
938 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939 rightMargin = marginRect.GetRight() - contentRect.GetRight();
940 topMargin = contentRect.GetTop() - marginRect.GetTop();
941 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
942
943 return true;
944 }
945
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
951 {
952 wxRect rect = availableParentSpace;
953 double scale = 1.0;
954 if (buffer)
955 scale = buffer->GetScale();
956
957 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
958
959 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
961
962 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
964
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 {
969 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
970 }
971 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 {
973 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
975 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
976 else
977 rect.x += x;
978 }
979
980 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 {
982 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
983 }
984 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 {
986 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
988 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
989 else
990 rect.y += y;
991 }
992
993 if (rect.GetWidth() > availableParentSpace.GetWidth())
994 rect.SetWidth(availableParentSpace.GetWidth());
995
996 return rect;
997 }
998
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream& stream)
1001 {
1002 stream << GetClassInfo()->GetClassName() << wxT("\n");
1003 stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
1004 stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
1005 }
1006
1007 // Gets the containing buffer
1008 wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1009 {
1010 const wxRichTextObject* obj = this;
1011 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
1012 obj = obj->GetParent();
1013 return wxDynamicCast(obj, wxRichTextBuffer);
1014 }
1015
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint wxRichTextObject::GetAbsolutePosition() const
1018 {
1019 wxPoint pt = GetPosition();
1020
1021 wxRichTextObject* p = GetParent();
1022 while (p)
1023 {
1024 pt = pt + p->GetPosition();
1025 p = p->GetParent();
1026 }
1027
1028 return pt;
1029 }
1030
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
1034 {
1035 if (!IsShown())
1036 return wxRICHTEXT_HITTEST_NONE;
1037
1038 wxRect rect = GetRect();
1039 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040 pt.y >= rect.y && pt.y < rect.y + rect.height)
1041 {
1042 *obj = this;
1043 *contextObj = GetParentContainer();
1044 textPosition = GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON;
1046 }
1047 else
1048 return wxRICHTEXT_HITTEST_NONE;
1049 }
1050
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
1054 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
1056 int style)
1057 {
1058 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
1059 wxRect originalAvailableRect = availableChildRect;
1060 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1061
1062 wxSize maxSize = GetMaxSize();
1063
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 // on this basis
1066 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
1067 {
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL);
1070 wxRichTextAttr newAttr(attr);
1071 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1073
1074 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
1075
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1081 {
1082 // centering, right-justification
1083 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
1084 {
1085 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1086 }
1087 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
1088 {
1089 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1090 }
1091 }
1092
1093 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1094 }
1095
1096 /*
1097 __________________
1098 | ____________ |
1099 | | | |
1100
1101
1102 */
1103
1104 return true;
1105 }
1106
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint& pt)
1109 {
1110 SetPosition(pt);
1111 }
1112
1113
1114 /*!
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1117 */
1118
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1120
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1122 wxRichTextObject(parent)
1123 {
1124 }
1125
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1127 {
1128 DeleteChildren();
1129 }
1130
1131 /// Get the nth child
1132 wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1133 {
1134 wxASSERT ( n < m_children.GetCount() );
1135
1136 return m_children.Item(n)->GetData();
1137 }
1138
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1141 {
1142 m_children.Append(child);
1143 child->SetParent(this);
1144 return m_children.GetCount() - 1;
1145 }
1146
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1149 {
1150 if (inFrontOf)
1151 {
1152 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1153 m_children.Insert(node, child);
1154 }
1155 else
1156 m_children.Insert(child);
1157 child->SetParent(this);
1158
1159 return true;
1160 }
1161
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1164 {
1165 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1166 if (node)
1167 {
1168 wxRichTextObject* obj = node->GetData();
1169 m_children.Erase(node);
1170 if (deleteChild)
1171 delete obj;
1172
1173 return true;
1174 }
1175 return false;
1176 }
1177
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1180 {
1181 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1182 while (node)
1183 {
1184 wxRichTextObjectList::compatibility_iterator oldNode = node;
1185
1186 wxRichTextObject* child = node->GetData();
1187 child->Dereference(); // Only delete if reference count is zero
1188
1189 node = node->GetNext();
1190 m_children.Erase(oldNode);
1191 }
1192
1193 return true;
1194 }
1195
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1198 {
1199 return m_children.GetCount();
1200 }
1201
1202 /// Copy
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1204 {
1205 wxRichTextObject::Copy(obj);
1206
1207 DeleteChildren();
1208
1209 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1210 while (node)
1211 {
1212 wxRichTextObject* child = node->GetData();
1213 wxRichTextObject* newChild = child->Clone();
1214 newChild->SetParent(this);
1215 m_children.Append(newChild);
1216
1217 node = node->GetNext();
1218 }
1219 }
1220
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1224 {
1225 if (!IsShown())
1226 return wxRICHTEXT_HITTEST_NONE;
1227
1228 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1229 while (node)
1230 {
1231 wxRichTextObject* child = node->GetData();
1232
1233 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1234 {
1235 // Just check if we hit the overall object
1236 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1237 if (ret != wxRICHTEXT_HITTEST_NONE)
1238 return ret;
1239 }
1240 else if (child->IsShown())
1241 {
1242 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1243 if (ret != wxRICHTEXT_HITTEST_NONE)
1244 return ret;
1245 }
1246
1247 node = node->GetNext();
1248 }
1249
1250 return wxRICHTEXT_HITTEST_NONE;
1251 }
1252
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
1255 {
1256 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1257 while (node)
1258 {
1259 wxRichTextObject* child = node->GetData();
1260
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
1266 return true;
1267
1268 node = node->GetNext();
1269 }
1270
1271 return false;
1272 }
1273
1274 /// Calculate range
1275 void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1276 {
1277 long current = start;
1278 long lastEnd = current;
1279
1280 if (IsTopLevel())
1281 {
1282 current = 0;
1283 lastEnd = 0;
1284 }
1285
1286 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1287 while (node)
1288 {
1289 wxRichTextObject* child = node->GetData();
1290 long childEnd = 0;
1291
1292 child->CalculateRange(current, childEnd);
1293 lastEnd = childEnd;
1294
1295 current = childEnd + 1;
1296
1297 node = node->GetNext();
1298 }
1299
1300 if (IsTopLevel())
1301 {
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1304 end = start;
1305 m_range.SetRange(start, start);
1306
1307 // An object with no children has zero length
1308 if (m_children.GetCount() == 0)
1309 lastEnd --;
1310 m_ownRange.SetRange(0, lastEnd);
1311 }
1312 else
1313 {
1314 end = lastEnd;
1315
1316 // An object with no children has zero length
1317 if (m_children.GetCount() == 0)
1318 end --;
1319
1320 m_range.SetRange(start, end);
1321 }
1322 }
1323
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1326 {
1327 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1328
1329 while (node)
1330 {
1331 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1332 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1333
1334 // Delete the range in each paragraph
1335
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1342
1343 if (!obj->GetRange().IsOutside(range))
1344 {
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj->IsTopLevel())
1347 obj->DeleteRange(range);
1348
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj->IsEmpty() ||
1351 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1352 {
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj, true);
1356 }
1357 }
1358
1359 node = next;
1360 }
1361
1362 return true;
1363 }
1364
1365 /// Get any text in this object for the given range
1366 wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1367 {
1368 wxString text;
1369 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1370 while (node)
1371 {
1372 wxRichTextObject* child = node->GetData();
1373 wxRichTextRange childRange = range;
1374 if (!child->GetRange().IsOutside(range))
1375 {
1376 childRange.LimitTo(child->GetRange());
1377
1378 wxString childText = child->GetTextForRange(childRange);
1379
1380 text += childText;
1381 }
1382 node = node->GetNext();
1383 }
1384
1385 return text;
1386 }
1387
1388 /// Get the child object at the given character position
1389 wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1390 {
1391 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1392 while (node)
1393 {
1394 wxRichTextObject* child = node->GetData();
1395 if (child->GetRange().GetStart() == pos)
1396 return child;
1397 node = node->GetNext();
1398 }
1399 return NULL;
1400 }
1401
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range)
1404 {
1405 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1406 while (node)
1407 {
1408 wxRichTextObject* child = node->GetData();
1409 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1410 {
1411 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1412 if (composite)
1413 composite->Defragment(context);
1414
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context.GetVirtualAttributesEnabled())
1419 {
1420 if (node->GetNext())
1421 {
1422 wxRichTextObject* nextChild = node->GetNext()->GetData();
1423 if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
1424 {
1425 nextChild->Dereference();
1426 m_children.Erase(node->GetNext());
1427 }
1428 else
1429 node = node->GetNext();
1430 }
1431 else
1432 node = node->GetNext();
1433 }
1434 else
1435 {
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1439 // at a time.
1440 wxRichTextObject* childAfterSplit = child;
1441 if (child->CanSplit(context))
1442 {
1443 childAfterSplit = child->Split(context);
1444 node = m_children.Find(childAfterSplit);
1445 }
1446
1447 if (node->GetNext())
1448 {
1449 wxRichTextObject* nextChild = node->GetNext()->GetData();
1450 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, const wxPoint& position, const wxSize& parentSize, 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), parentSize, 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, const wxPoint& position, const wxSize& parentSize, 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, parentSize);
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(), parentRect.GetSize(), & 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), parentRect.GetSize(), & 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, wxPoint(0,0), parentRect.GetSize());
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(), parentRect.GetSize());
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.GetSize(), 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), parentRect.GetSize(), & 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 #if 0
4932 ||
4933 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4934 #endif
4935 )
4936 {
4937 long wrapPosition = 0;
4938 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4939 wrapPosition = child->GetRange().GetEnd();
4940 else
4941
4942 // Find a place to wrap. This may walk back to previous children,
4943 // for example if a word spans several objects.
4944 // Note: one object must contains only one wxTextAtrr, so the line height will not
4945 // change inside one object. Thus, we can pass the remain line width to the
4946 // FindWrapPosition function.
4947 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
4948 {
4949 // If the function failed, just cut it off at the end of this child.
4950 wrapPosition = child->GetRange().GetEnd();
4951 }
4952
4953 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4954 if (wrapPosition <= lastCompletedEndPos)
4955 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4956
4957 // Line end position shouldn't be the same as the end, or greater.
4958 if (wrapPosition >= GetRange().GetEnd())
4959 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
4960
4961 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4962
4963 // Let's find the actual size of the current line now
4964 wxSize actualSize;
4965 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4966
4967 childDescent = 0;
4968
4969 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4970 if (!child->IsEmpty())
4971 {
4972 // Get height only, then the width using the partial extents
4973 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4974 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4975 }
4976 else
4977 #endif
4978 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
4979
4980 currentWidth = actualSize.x;
4981
4982 // The descent for the whole line at this point, is the correct max descent
4983 maxDescent = childDescent;
4984 // Maximum ascent
4985 maxAscent = actualSize.y-childDescent;
4986
4987 // lineHeight is given by the height for the whole line, since it will
4988 // take into account ascend/descend.
4989 lineHeight = actualSize.y;
4990
4991 if (lineHeight == 0 && buffer)
4992 {
4993 wxFont font(buffer->GetFontTable().FindFont(attr));
4994 wxCheckSetFont(dc, font);
4995 lineHeight = dc.GetCharHeight();
4996 }
4997
4998 if (maxDescent == 0)
4999 {
5000 int w, h;
5001 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5002 }
5003
5004 // Add a new line
5005 wxRichTextLine* line = AllocateLine(lineCount);
5006
5007 // Set relative range so we won't have to change line ranges when paragraphs are moved
5008 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5009 line->SetPosition(currentPosition);
5010 line->SetSize(wxSize(currentWidth, lineHeight));
5011 line->SetDescent(maxDescent);
5012
5013 maxHeight = currentPosition.y + lineHeight;
5014
5015 // Now move down a line. TODO: add margins, spacing
5016 currentPosition.y += lineHeight;
5017 currentPosition.y += lineSpacing;
5018 maxDescent = 0;
5019 maxAscent = 0;
5020 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5021 currentWidth = 0;
5022
5023 lineCount ++;
5024
5025 // TODO: account for zero-length objects
5026 // wxASSERT(wrapPosition > lastCompletedEndPos);
5027
5028 lastEndPos = wrapPosition;
5029 lastCompletedEndPos = lastEndPos;
5030
5031 lineHeight = 0;
5032
5033 if (wrapPosition < GetRange().GetEnd()-1)
5034 {
5035 // May need to set the node back to a previous one, due to searching back in wrapping
5036 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5037 if (childAfterWrapPosition)
5038 node = m_children.Find(childAfterWrapPosition);
5039 else
5040 node = node->GetNext();
5041 }
5042 else
5043 node = node->GetNext();
5044
5045 // Apply paragraph styles such as alignment to the wrapped line
5046 ApplyParagraphStyle(line, attr, availableRect, dc);
5047 }
5048 else
5049 {
5050 // We still fit, so don't add a line, and keep going
5051 currentWidth += childSize.x;
5052
5053 if (childDescent == 0)
5054 {
5055 // An object with a zero descend value wants to take up the whole
5056 // height regardless of baseline
5057 lineHeight = wxMax(lineHeight, childSize.y);
5058 }
5059 else
5060 {
5061 maxDescent = wxMax(childDescent, maxDescent);
5062 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5063 }
5064
5065 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5066
5067 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5068 lastEndPos = child->GetRange().GetEnd();
5069
5070 node = node->GetNext();
5071 }
5072 }
5073
5074 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5075
5076 // Add the last line - it's the current pos -> last para pos
5077 // Substract -1 because the last position is always the end-paragraph position.
5078 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
5079 {
5080 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
5081
5082 wxRichTextLine* line = AllocateLine(lineCount);
5083
5084 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
5085
5086 // Set relative range so we won't have to change line ranges when paragraphs are moved
5087 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5088
5089 line->SetPosition(currentPosition);
5090
5091 if (lineHeight == 0 && buffer)
5092 {
5093 wxFont font(buffer->GetFontTable().FindFont(attr));
5094 wxCheckSetFont(dc, font);
5095 lineHeight = dc.GetCharHeight();
5096 }
5097
5098 if (maxDescent == 0)
5099 {
5100 int w, h;
5101 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5102 }
5103
5104 line->SetSize(wxSize(currentWidth, lineHeight));
5105 line->SetDescent(maxDescent);
5106 currentPosition.y += lineHeight;
5107 currentPosition.y += lineSpacing;
5108 lineCount ++;
5109 }
5110
5111 // Remove remaining unused line objects, if any
5112 ClearUnusedLines(lineCount);
5113
5114 // We need to add back the margins etc.
5115 {
5116 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5117 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
5118 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5119 SetCachedSize(marginRect.GetSize());
5120 }
5121
5122 // The maximum size is the length of the paragraph stretched out into a line.
5123 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5124 // this size. TODO: take into account line breaks.
5125 {
5126 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5127 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
5128 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5129 SetMaxSize(marginRect.GetSize());
5130 }
5131
5132 // Find the greatest minimum size. Currently we only look at non-text objects,
5133 // which isn't ideal but it would be slow to find the maximum word width to
5134 // use as the minimum.
5135 {
5136 int minWidth = 0;
5137 node = m_children.GetFirst();
5138 while (node)
5139 {
5140 wxRichTextObject* child = node->GetData();
5141
5142 // If floating, ignore. We already laid out floats.
5143 // Also ignore if empty object, except if we haven't got any
5144 // size yet.
5145 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
5146 {
5147 if (child->GetCachedSize().x > minWidth)
5148 minWidth = child->GetMinSize().x;
5149 }
5150 node = node->GetNext();
5151 }
5152
5153 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5154 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
5155 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5156 SetMinSize(marginRect.GetSize());
5157 }
5158
5159 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5160 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5161 // Use the text extents to calculate the size of each fragment in each line
5162 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5163 while (lineNode)
5164 {
5165 wxRichTextLine* line = lineNode->GetData();
5166 wxRichTextRange lineRange = line->GetAbsoluteRange();
5167
5168 // Loop through objects until we get to the one within range
5169 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5170
5171 while (node2)
5172 {
5173 wxRichTextObject* child = node2->GetData();
5174
5175 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
5176 {
5177 wxRichTextRange rangeToUse = lineRange;
5178 rangeToUse.LimitTo(child->GetRange());
5179
5180 // Find the size of the child from the text extents, and store in an array
5181 // for drawing later
5182 int left = 0;
5183 if (rangeToUse.GetStart() > GetRange().GetStart())
5184 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5185 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5186 int sz = right - left;
5187 line->GetObjectSizes().Add(sz);
5188 }
5189 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5190 // Can break out of inner loop now since we've passed this line's range
5191 break;
5192
5193 node2 = node2->GetNext();
5194 }
5195
5196 lineNode = lineNode->GetNext();
5197 }
5198 #endif
5199 #endif
5200
5201 return true;
5202 }
5203
5204 /// Apply paragraph styles, such as centering, to wrapped lines
5205 /// TODO: take into account box attributes, possibly
5206 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5207 {
5208 if (!attr.HasAlignment())
5209 return;
5210
5211 wxPoint pos = line->GetPosition();
5212 wxPoint originalPos = pos;
5213 wxSize size = line->GetSize();
5214
5215 // centering, right-justification
5216 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
5217 {
5218 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5219 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5220 line->SetPosition(pos);
5221 }
5222 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
5223 {
5224 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5225 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5226 line->SetPosition(pos);
5227 }
5228
5229 if (pos != originalPos)
5230 {
5231 wxPoint inc = pos - originalPos;
5232
5233 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5234
5235 while (node)
5236 {
5237 wxRichTextObject* child = node->GetData();
5238 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5239 child->Move(child->GetPosition() + inc);
5240
5241 node = node->GetNext();
5242 }
5243 }
5244 }
5245
5246 /// Insert text at the given position
5247 bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5248 {
5249 wxRichTextObject* childToUse = NULL;
5250 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5251
5252 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5253 while (node)
5254 {
5255 wxRichTextObject* child = node->GetData();
5256 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5257 {
5258 childToUse = child;
5259 nodeToUse = node;
5260 break;
5261 }
5262
5263 node = node->GetNext();
5264 }
5265
5266 if (childToUse)
5267 {
5268 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5269 if (textObject)
5270 {
5271 int posInString = pos - textObject->GetRange().GetStart();
5272
5273 wxString newText = textObject->GetText().Mid(0, posInString) +
5274 text + textObject->GetText().Mid(posInString);
5275 textObject->SetText(newText);
5276
5277 int textLength = text.length();
5278
5279 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5280 textObject->GetRange().GetEnd() + textLength));
5281
5282 // Increment the end range of subsequent fragments in this paragraph.
5283 // We'll set the paragraph range itself at a higher level.
5284
5285 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5286 while (node)
5287 {
5288 wxRichTextObject* child = node->GetData();
5289 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5290 textObject->GetRange().GetEnd() + textLength));
5291
5292 node = node->GetNext();
5293 }
5294
5295 return true;
5296 }
5297 else
5298 {
5299 // TODO: if not a text object, insert at closest position, e.g. in front of it
5300 }
5301 }
5302 else
5303 {
5304 // Add at end.
5305 // Don't pass parent initially to suppress auto-setting of parent range.
5306 // We'll do that at a higher level.
5307 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5308
5309 AppendChild(textObject);
5310 return true;
5311 }
5312
5313 return false;
5314 }
5315
5316 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5317 {
5318 wxRichTextCompositeObject::Copy(obj);
5319 }
5320
5321 /// Clear the cached lines
5322 void wxRichTextParagraph::ClearLines()
5323 {
5324 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5325 }
5326
5327 /// Get/set the object size for the given range. Returns false if the range
5328 /// is invalid for this object.
5329 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
5330 {
5331 if (!range.IsWithin(GetRange()))
5332 return false;
5333
5334 if (flags & wxRICHTEXT_UNFORMATTED)
5335 {
5336 // Just use unformatted data, assume no line breaks
5337 wxSize sz;
5338
5339 wxArrayInt childExtents;
5340 wxArrayInt* p;
5341 if (partialExtents)
5342 p = & childExtents;
5343 else
5344 p = NULL;
5345
5346 int maxDescent = 0;
5347 int maxAscent = 0;
5348 int maxLineHeight = 0;
5349
5350 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5351 while (node)
5352 {
5353 wxRichTextObject* child = node->GetData();
5354 if (!child->GetRange().IsOutside(range))
5355 {
5356 // Floating objects have a zero size within the paragraph.
5357 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5358 {
5359 if (partialExtents)
5360 {
5361 int lastSize;
5362 if (partialExtents->GetCount() > 0)
5363 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5364 else
5365 lastSize = 0;
5366
5367 partialExtents->Add(0 /* zero size */ + lastSize);
5368 }
5369 }
5370 else
5371 {
5372 wxSize childSize;
5373
5374 wxRichTextRange rangeToUse = range;
5375 rangeToUse.LimitTo(child->GetRange());
5376 int childDescent = 0;
5377
5378 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5379 // but it's only going to be used after caching has taken place.
5380 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
5381 {
5382 childDescent = child->GetDescent();
5383 childSize = child->GetCachedSize();
5384
5385 if (childDescent == 0)
5386 {
5387 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5388 }
5389 else
5390 {
5391 maxDescent = wxMax(maxDescent, childDescent);
5392 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5393 }
5394
5395 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5396
5397 sz.y = wxMax(sz.y, maxLineHeight);
5398 sz.x += childSize.x;
5399 descent = maxDescent;
5400 }
5401 else if (child->IsTopLevel())
5402 {
5403 childDescent = child->GetDescent();
5404 childSize = child->GetCachedSize();
5405
5406 if (childDescent == 0)
5407 {
5408 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5409 }
5410 else
5411 {
5412 maxDescent = wxMax(maxDescent, childDescent);
5413 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5414 }
5415
5416 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5417
5418 sz.y = wxMax(sz.y, maxLineHeight);
5419 sz.x += childSize.x;
5420 descent = maxDescent;
5421
5422 // FIXME: this won't change the original values.
5423 // Should we be calling GetRangeSize above instead of using cached values?
5424 #if 0
5425 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5426 {
5427 child->SetCachedSize(childSize);
5428 child->SetDescent(childDescent);
5429 }
5430 #endif
5431
5432 if (partialExtents)
5433 {
5434 int lastSize;
5435 if (partialExtents->GetCount() > 0)
5436 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5437 else
5438 lastSize = 0;
5439
5440 partialExtents->Add(childSize.x + lastSize);
5441 }
5442 }
5443 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
5444 {
5445 if (childDescent == 0)
5446 {
5447 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5448 }
5449 else
5450 {
5451 maxDescent = wxMax(maxDescent, childDescent);
5452 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5453 }
5454
5455 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5456
5457 sz.y = wxMax(sz.y, maxLineHeight);
5458 sz.x += childSize.x;
5459 descent = maxDescent;
5460
5461 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5462 {
5463 child->SetCachedSize(childSize);
5464 child->SetDescent(childDescent);
5465 }
5466
5467 if (partialExtents)
5468 {
5469 int lastSize;
5470 if (partialExtents->GetCount() > 0)
5471 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5472 else
5473 lastSize = 0;
5474
5475 size_t i;
5476 for (i = 0; i < childExtents.GetCount(); i++)
5477 {
5478 partialExtents->Add(childExtents[i] + lastSize);
5479 }
5480 }
5481 }
5482 }
5483
5484 if (p)
5485 p->Clear();
5486 }
5487
5488 node = node->GetNext();
5489 }
5490 size = sz;
5491 }
5492 else
5493 {
5494 // Use formatted data, with line breaks
5495 wxSize sz;
5496
5497 // We're going to loop through each line, and then for each line,
5498 // call GetRangeSize for the fragment that comprises that line.
5499 // Only we have to do that multiple times within the line, because
5500 // the line may be broken into pieces. For now ignore line break commands
5501 // (so we can assume that getting the unformatted size for a fragment
5502 // within a line is the actual size)
5503
5504 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5505 while (node)
5506 {
5507 wxRichTextLine* line = node->GetData();
5508 wxRichTextRange lineRange = line->GetAbsoluteRange();
5509 if (!lineRange.IsOutside(range))
5510 {
5511 int maxDescent = 0;
5512 int maxAscent = 0;
5513 int maxLineHeight = 0;
5514 int maxLineWidth = 0;
5515
5516 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5517 while (node2)
5518 {
5519 wxRichTextObject* child = node2->GetData();
5520
5521 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5522 {
5523 wxRichTextRange rangeToUse = lineRange;
5524 rangeToUse.LimitTo(child->GetRange());
5525 if (child->IsTopLevel())
5526 rangeToUse = child->GetOwnRange();
5527
5528 wxSize childSize;
5529 int childDescent = 0;
5530 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
5531 {
5532 if (childDescent == 0)
5533 {
5534 // Assume that if descent is zero, this child can occupy the full line height
5535 // and does not need space for the line's maximum descent. So we influence
5536 // the overall max line height only.
5537 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5538 }
5539 else
5540 {
5541 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5542 maxDescent = wxMax(maxAscent, childDescent);
5543 }
5544 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5545 maxLineWidth += childSize.x;
5546 }
5547 }
5548
5549 node2 = node2->GetNext();
5550 }
5551
5552 descent = wxMax(descent, maxDescent);
5553
5554 // Increase size by a line (TODO: paragraph spacing)
5555 sz.y += maxLineHeight;
5556 sz.x = wxMax(sz.x, maxLineWidth);
5557 }
5558 node = node->GetNext();
5559 }
5560 size = sz;
5561 }
5562 return true;
5563 }
5564
5565 /// Finds the absolute position and row height for the given character position
5566 bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5567 {
5568 if (index == -1)
5569 {
5570 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5571 if (line)
5572 *height = line->GetSize().y;
5573 else
5574 *height = dc.GetCharHeight();
5575
5576 // -1 means 'the start of the buffer'.
5577 pt = GetPosition();
5578 if (line)
5579 pt = pt + line->GetPosition();
5580
5581 return true;
5582 }
5583
5584 // The final position in a paragraph is taken to mean the position
5585 // at the start of the next paragraph.
5586 if (index == GetRange().GetEnd())
5587 {
5588 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5589 wxASSERT( parent != NULL );
5590
5591 // Find the height at the next paragraph, if any
5592 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5593 if (line)
5594 {
5595 *height = line->GetSize().y;
5596 pt = line->GetAbsolutePosition();
5597 }
5598 else
5599 {
5600 *height = dc.GetCharHeight();
5601 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5602 pt = wxPoint(indent, GetCachedSize().y);
5603 }
5604
5605 return true;
5606 }
5607
5608 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5609 return false;
5610
5611 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5612 while (node)
5613 {
5614 wxRichTextLine* line = node->GetData();
5615 wxRichTextRange lineRange = line->GetAbsoluteRange();
5616 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5617 {
5618 // If this is the last point in the line, and we're forcing the
5619 // returned value to be the start of the next line, do the required
5620 // thing.
5621 if (index == lineRange.GetEnd() && forceLineStart)
5622 {
5623 if (node->GetNext())
5624 {
5625 wxRichTextLine* nextLine = node->GetNext()->GetData();
5626 *height = nextLine->GetSize().y;
5627 pt = nextLine->GetAbsolutePosition();
5628 return true;
5629 }
5630 }
5631
5632 pt.y = line->GetPosition().y + GetPosition().y;
5633
5634 wxRichTextRange r(lineRange.GetStart(), index);
5635 wxSize rangeSize;
5636 int descent = 0;
5637
5638 // We find the size of the line up to this point,
5639 // then we can add this size to the line start position and
5640 // paragraph start position to find the actual position.
5641
5642 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5643 {
5644 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5645 *height = line->GetSize().y;
5646
5647 return true;
5648 }
5649
5650 }
5651
5652 node = node->GetNext();
5653 }
5654
5655 return false;
5656 }
5657
5658 /// Hit-testing: returns a flag indicating hit test details, plus
5659 /// information about position
5660 int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5661 {
5662 if (!IsShown())
5663 return wxRICHTEXT_HITTEST_NONE;
5664
5665 // If we're in the top-level container, then we can return
5666 // a suitable hit test code even if the point is outside the container area,
5667 // so that we can position the caret sensibly even if we don't
5668 // click on valid content. If we're not at the top-level, and the point
5669 // is not within this paragraph object, then we don't want to stop more
5670 // precise hit-testing from working prematurely, so return immediately.
5671 // NEW STRATEGY: use the parent boundary to test whether we're in the
5672 // right region, not the paragraph, since the paragraph may be positioned
5673 // some way in from where the user clicks.
5674 {
5675 long tmpPos;
5676 wxRichTextObject* tempObj, *tempContextObj;
5677 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
5678 return wxRICHTEXT_HITTEST_NONE;
5679 }
5680
5681 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5682 while (objNode)
5683 {
5684 wxRichTextObject* child = objNode->GetData();
5685 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5686 // and also, if this seems composite but actually is marked as atomic,
5687 // don't recurse.
5688 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5689 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
5690 {
5691 {
5692 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
5693 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5694 return hitTest;
5695 }
5696 }
5697
5698 objNode = objNode->GetNext();
5699 }
5700
5701 wxPoint paraPos = GetPosition();
5702
5703 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5704 while (node)
5705 {
5706 wxRichTextLine* line = node->GetData();
5707 wxPoint linePos = paraPos + line->GetPosition();
5708 wxSize lineSize = line->GetSize();
5709 wxRichTextRange lineRange = line->GetAbsoluteRange();
5710
5711 if (pt.y <= linePos.y + lineSize.y)
5712 {
5713 if (pt.x < linePos.x)
5714 {
5715 textPosition = lineRange.GetStart();
5716 *obj = FindObjectAtPosition(textPosition);
5717 *contextObj = GetContainer();
5718 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5719 }
5720 else if (pt.x >= (linePos.x + lineSize.x))
5721 {
5722 textPosition = lineRange.GetEnd();
5723 *obj = FindObjectAtPosition(textPosition);
5724 *contextObj = GetContainer();
5725 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5726 }
5727 else
5728 {
5729 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5730 wxArrayInt partialExtents;
5731
5732 wxSize paraSize;
5733 int paraDescent;
5734
5735 // This calculates the partial text extents
5736 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
5737
5738 int lastX = linePos.x;
5739 size_t i;
5740 for (i = 0; i < partialExtents.GetCount(); i++)
5741 {
5742 int nextX = partialExtents[i] + linePos.x;
5743
5744 if (pt.x >= lastX && pt.x <= nextX)
5745 {
5746 textPosition = i + lineRange.GetStart(); // minus 1?
5747
5748 *obj = FindObjectAtPosition(textPosition);
5749 *contextObj = GetContainer();
5750
5751 // So now we know it's between i-1 and i.
5752 // Let's see if we can be more precise about
5753 // which side of the position it's on.
5754
5755 int midPoint = (nextX + lastX)/2;
5756 if (pt.x >= midPoint)
5757 return wxRICHTEXT_HITTEST_AFTER;
5758 else
5759 return wxRICHTEXT_HITTEST_BEFORE;
5760 }
5761
5762 lastX = nextX;
5763 }
5764 #else
5765 long i;
5766 int lastX = linePos.x;
5767 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5768 {
5769 wxSize childSize;
5770 int descent = 0;
5771
5772 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
5773
5774 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5775
5776 int nextX = childSize.x + linePos.x;
5777
5778 if (pt.x >= lastX && pt.x <= nextX)
5779 {
5780 textPosition = i;
5781
5782 *obj = FindObjectAtPosition(textPosition);
5783 *contextObj = GetContainer();
5784
5785 // So now we know it's between i-1 and i.
5786 // Let's see if we can be more precise about
5787 // which side of the position it's on.
5788
5789 int midPoint = (nextX + lastX)/2;
5790 if (pt.x >= midPoint)
5791 return wxRICHTEXT_HITTEST_AFTER;
5792 else
5793 return wxRICHTEXT_HITTEST_BEFORE;
5794 }
5795 else
5796 {
5797 lastX = nextX;
5798 }
5799 }
5800 #endif
5801 }
5802 }
5803
5804 node = node->GetNext();
5805 }
5806
5807 return wxRICHTEXT_HITTEST_NONE;
5808 }
5809
5810 /// Split an object at this position if necessary, and return
5811 /// the previous object, or NULL if inserting at beginning.
5812 wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5813 {
5814 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5815 while (node)
5816 {
5817 wxRichTextObject* child = node->GetData();
5818
5819 if (pos == child->GetRange().GetStart())
5820 {
5821 if (previousObject)
5822 {
5823 if (node->GetPrevious())
5824 *previousObject = node->GetPrevious()->GetData();
5825 else
5826 *previousObject = NULL;
5827 }
5828
5829 return child;
5830 }
5831
5832 if (child->GetRange().Contains(pos))
5833 {
5834 // This should create a new object, transferring part of
5835 // the content to the old object and the rest to the new object.
5836 wxRichTextObject* newObject = child->DoSplit(pos);
5837
5838 // If we couldn't split this object, just insert in front of it.
5839 if (!newObject)
5840 {
5841 // Maybe this is an empty string, try the next one
5842 // return child;
5843 }
5844 else
5845 {
5846 // Insert the new object after 'child'
5847 if (node->GetNext())
5848 m_children.Insert(node->GetNext(), newObject);
5849 else
5850 m_children.Append(newObject);
5851 newObject->SetParent(this);
5852
5853 if (previousObject)
5854 *previousObject = child;
5855
5856 return newObject;
5857 }
5858 }
5859
5860 node = node->GetNext();
5861 }
5862 if (previousObject)
5863 *previousObject = NULL;
5864 return NULL;
5865 }
5866
5867 /// Move content to a list from obj on
5868 void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5869 {
5870 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5871 while (node)
5872 {
5873 wxRichTextObject* child = node->GetData();
5874 list.Append(child);
5875
5876 wxRichTextObjectList::compatibility_iterator oldNode = node;
5877
5878 node = node->GetNext();
5879
5880 m_children.DeleteNode(oldNode);
5881 }
5882 }
5883
5884 /// Add content back from list
5885 void wxRichTextParagraph::MoveFromList(wxList& list)
5886 {
5887 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5888 {
5889 AppendChild((wxRichTextObject*) node->GetData());
5890 }
5891 }
5892
5893 /// Calculate range
5894 void wxRichTextParagraph::CalculateRange(long start, long& end)
5895 {
5896 wxRichTextCompositeObject::CalculateRange(start, end);
5897
5898 // Add one for end of paragraph
5899 end ++;
5900
5901 m_range.SetRange(start, end);
5902 }
5903
5904 /// Find the object at the given position
5905 wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5906 {
5907 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5908 while (node)
5909 {
5910 wxRichTextObject* obj = node->GetData();
5911 if (obj->GetRange().Contains(position) ||
5912 obj->GetRange().GetStart() == position ||
5913 obj->GetRange().GetEnd() == position)
5914 return obj;
5915
5916 node = node->GetNext();
5917 }
5918 return NULL;
5919 }
5920
5921 /// Get the plain text searching from the start or end of the range.
5922 /// The resulting string may be shorter than the range given.
5923 bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5924 {
5925 text = wxEmptyString;
5926
5927 if (fromStart)
5928 {
5929 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5930 while (node)
5931 {
5932 wxRichTextObject* obj = node->GetData();
5933 if (!obj->GetRange().IsOutside(range))
5934 {
5935 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5936 if (textObj)
5937 {
5938 text += textObj->GetTextForRange(range);
5939 }
5940 else
5941 {
5942 text += wxT(" ");
5943 }
5944 }
5945
5946 node = node->GetNext();
5947 }
5948 }
5949 else
5950 {
5951 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5952 while (node)
5953 {
5954 wxRichTextObject* obj = node->GetData();
5955 if (!obj->GetRange().IsOutside(range))
5956 {
5957 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5958 if (textObj)
5959 {
5960 text = textObj->GetTextForRange(range) + text;
5961 }
5962 else
5963 {
5964 text = wxT(" ") + text;
5965 }
5966 }
5967
5968 node = node->GetPrevious();
5969 }
5970 }
5971
5972 return true;
5973 }
5974
5975 /// Find a suitable wrap position.
5976 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5977 {
5978 if (range.GetLength() <= 0)
5979 return false;
5980
5981 // Find the first position where the line exceeds the available space.
5982 wxSize sz;
5983 long breakPosition = range.GetEnd();
5984
5985 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5986 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5987 {
5988 int widthBefore;
5989
5990 if (range.GetStart() > GetRange().GetStart())
5991 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5992 else
5993 widthBefore = 0;
5994
5995 size_t i;
5996 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5997 {
5998 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
5999
6000 if (widthFromStartOfThisRange > availableSpace)
6001 {
6002 breakPosition = i-1;
6003 break;
6004 }
6005 }
6006 }
6007 else
6008 #endif
6009 {
6010 // Binary chop for speed
6011 long minPos = range.GetStart();
6012 long maxPos = range.GetEnd();
6013 while (true)
6014 {
6015 if (minPos == maxPos)
6016 {
6017 int descent = 0;
6018 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6019
6020 if (sz.x > availableSpace)
6021 breakPosition = minPos - 1;
6022 break;
6023 }
6024 else if ((maxPos - minPos) == 1)
6025 {
6026 int descent = 0;
6027 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6028
6029 if (sz.x > availableSpace)
6030 breakPosition = minPos - 1;
6031 else
6032 {
6033 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6034 if (sz.x > availableSpace)
6035 breakPosition = maxPos-1;
6036 }
6037 break;
6038 }
6039 else
6040 {
6041 long nextPos = minPos + ((maxPos - minPos) / 2);
6042
6043 int descent = 0;
6044 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6045
6046 if (sz.x > availableSpace)
6047 {
6048 maxPos = nextPos;
6049 }
6050 else
6051 {
6052 minPos = nextPos;
6053 }
6054 }
6055 }
6056 }
6057
6058 // Now we know the last position on the line.
6059 // Let's try to find a word break.
6060
6061 wxString plainText;
6062 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6063 {
6064 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6065 if (newLinePos != wxNOT_FOUND)
6066 {
6067 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6068 }
6069 else
6070 {
6071 int spacePos = plainText.Find(wxT(' '), true);
6072 int tabPos = plainText.Find(wxT('\t'), true);
6073 int pos = wxMax(spacePos, tabPos);
6074 if (pos != wxNOT_FOUND)
6075 {
6076 int positionsFromEndOfString = plainText.length() - pos - 1;
6077 breakPosition = breakPosition - positionsFromEndOfString;
6078 }
6079 }
6080 }
6081
6082 wrapPosition = breakPosition;
6083
6084 return true;
6085 }
6086
6087 /// Get the bullet text for this paragraph.
6088 wxString wxRichTextParagraph::GetBulletText()
6089 {
6090 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6091 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6092 return wxEmptyString;
6093
6094 int number = GetAttributes().GetBulletNumber();
6095
6096 wxString text;
6097 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
6098 {
6099 text.Printf(wxT("%d"), number);
6100 }
6101 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6102 {
6103 // TODO: Unicode, and also check if number > 26
6104 text.Printf(wxT("%c"), (wxChar) (number+64));
6105 }
6106 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6107 {
6108 // TODO: Unicode, and also check if number > 26
6109 text.Printf(wxT("%c"), (wxChar) (number+96));
6110 }
6111 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6112 {
6113 text = wxRichTextDecimalToRoman(number);
6114 }
6115 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6116 {
6117 text = wxRichTextDecimalToRoman(number);
6118 text.MakeLower();
6119 }
6120 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6121 {
6122 text = GetAttributes().GetBulletText();
6123 }
6124
6125 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6126 {
6127 // The outline style relies on the text being computed statically,
6128 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6129 // should be stored in the attributes; if not, just use the number for this
6130 // level, as previously computed.
6131 if (!GetAttributes().GetBulletText().IsEmpty())
6132 text = GetAttributes().GetBulletText();
6133 }
6134
6135 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6136 {
6137 text = wxT("(") + text + wxT(")");
6138 }
6139 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6140 {
6141 text = text + wxT(")");
6142 }
6143
6144 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6145 {
6146 text += wxT(".");
6147 }
6148
6149 return text;
6150 }
6151
6152 /// Allocate or reuse a line object
6153 wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6154 {
6155 if (pos < (int) m_cachedLines.GetCount())
6156 {
6157 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6158 line->Init(this);
6159 return line;
6160 }
6161 else
6162 {
6163 wxRichTextLine* line = new wxRichTextLine(this);
6164 m_cachedLines.Append(line);
6165 return line;
6166 }
6167 }
6168
6169 /// Clear remaining unused line objects, if any
6170 bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6171 {
6172 int cachedLineCount = m_cachedLines.GetCount();
6173 if ((int) cachedLineCount > lineCount)
6174 {
6175 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6176 {
6177 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6178 wxRichTextLine* line = node->GetData();
6179 m_cachedLines.Erase(node);
6180 delete line;
6181 }
6182 }
6183 return true;
6184 }
6185
6186 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6187 /// retrieve the actual style.
6188 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
6189 {
6190 wxRichTextAttr attr;
6191 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6192 if (buf)
6193 {
6194 attr = buf->GetBasicStyle();
6195 if (!includingBoxAttr)
6196 {
6197 attr.GetTextBoxAttr().Reset();
6198 // The background colour will be painted by the container, and we don't
6199 // want to unnecessarily overwrite the background when we're drawing text
6200 // because this may erase the guideline (which appears just under the text
6201 // if there's no padding).
6202 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6203 }
6204 wxRichTextApplyStyle(attr, GetAttributes());
6205 }
6206 else
6207 attr = GetAttributes();
6208
6209 wxRichTextApplyStyle(attr, contentStyle);
6210 return attr;
6211 }
6212
6213 /// Get combined attributes of the base style and paragraph style.
6214 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
6215 {
6216 wxRichTextAttr attr;
6217 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6218 if (buf)
6219 {
6220 attr = buf->GetBasicStyle();
6221 if (!includingBoxAttr)
6222 attr.GetTextBoxAttr().Reset();
6223 wxRichTextApplyStyle(attr, GetAttributes());
6224 }
6225 else
6226 attr = GetAttributes();
6227
6228 return attr;
6229 }
6230
6231 // Create default tabstop array
6232 void wxRichTextParagraph::InitDefaultTabs()
6233 {
6234 // create a default tab list at 10 mm each.
6235 for (int i = 0; i < 20; ++i)
6236 {
6237 sm_defaultTabs.Add(i*100);
6238 }
6239 }
6240
6241 // Clear default tabstop array
6242 void wxRichTextParagraph::ClearDefaultTabs()
6243 {
6244 sm_defaultTabs.Clear();
6245 }
6246
6247 void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
6248 {
6249 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6250 while (node)
6251 {
6252 wxRichTextObject* anchored = node->GetData();
6253 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
6254 {
6255 int x = 0;
6256 wxRichTextAttr parentAttr(GetAttributes());
6257 context.ApplyVirtualAttributes(parentAttr, this);
6258 #if 1
6259 // 27-09-2012
6260 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6261
6262 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6263 parentAttr, anchored->GetAttributes(),
6264 parentRect, availableSpace,
6265 style);
6266 wxSize size = anchored->GetCachedSize();
6267 #else
6268 wxSize size;
6269 int descent = 0;
6270 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
6271 #endif
6272
6273 int offsetY = 0;
6274 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6275 {
6276 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6277 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6278 {
6279 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6280 }
6281 }
6282
6283 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
6284
6285 /* Update the offset */
6286 int newOffsetY = pos - rect.y;
6287 if (newOffsetY != offsetY)
6288 {
6289 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6290 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6291 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6292 }
6293
6294 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
6295 x = rect.x;
6296 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
6297 x = rect.x + rect.width - size.x;
6298
6299 //anchored->SetPosition(wxPoint(x, pos));
6300 anchored->Move(wxPoint(x, pos)); // should move children
6301 anchored->SetCachedSize(size);
6302 floatCollector->CollectFloat(this, anchored);
6303 }
6304
6305 node = node->GetNext();
6306 }
6307 }
6308
6309 // Get the first position from pos that has a line break character.
6310 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6311 {
6312 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6313 while (node)
6314 {
6315 wxRichTextObject* obj = node->GetData();
6316 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6317 {
6318 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6319 if (textObj)
6320 {
6321 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6322 if (breakPos > -1)
6323 return breakPos;
6324 }
6325 }
6326 node = node->GetNext();
6327 }
6328 return -1;
6329 }
6330
6331 /*!
6332 * wxRichTextLine
6333 * This object represents a line in a paragraph, and stores
6334 * offsets from the start of the paragraph representing the
6335 * start and end positions of the line.
6336 */
6337
6338 wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6339 {
6340 Init(parent);
6341 }
6342
6343 /// Initialisation
6344 void wxRichTextLine::Init(wxRichTextParagraph* parent)
6345 {
6346 m_parent = parent;
6347 m_range.SetRange(-1, -1);
6348 m_pos = wxPoint(0, 0);
6349 m_size = wxSize(0, 0);
6350 m_descent = 0;
6351 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6352 m_objectSizes.Clear();
6353 #endif
6354 }
6355
6356 /// Copy
6357 void wxRichTextLine::Copy(const wxRichTextLine& obj)
6358 {
6359 m_range = obj.m_range;
6360 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6361 m_objectSizes = obj.m_objectSizes;
6362 #endif
6363 }
6364
6365 /// Get the absolute object position
6366 wxPoint wxRichTextLine::GetAbsolutePosition() const
6367 {
6368 return m_parent->GetPosition() + m_pos;
6369 }
6370
6371 /// Get the absolute range
6372 wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6373 {
6374 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6375 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6376 return range;
6377 }
6378
6379 /*!
6380 * wxRichTextPlainText
6381 * This object represents a single piece of text.
6382 */
6383
6384 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6385
6386 wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
6387 wxRichTextObject(parent)
6388 {
6389 if (style)
6390 SetAttributes(*style);
6391
6392 m_text = text;
6393 }
6394
6395 #define USE_KERNING_FIX 1
6396
6397 // If insufficient tabs are defined, this is the tab width used
6398 #define WIDTH_FOR_DEFAULT_TABS 50
6399
6400 /// Draw the item
6401 bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
6402 {
6403 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6404 wxASSERT (para != NULL);
6405
6406 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6407 context.ApplyVirtualAttributes(textAttr, this);
6408
6409 // Let's make the assumption for now that for content in a paragraph, including
6410 // text, we never have a discontinuous selection. So we only deal with a
6411 // single range.
6412 wxRichTextRange selectionRange;
6413 if (selection.IsValid())
6414 {
6415 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6416 if (selectionRanges.GetCount() > 0)
6417 selectionRange = selectionRanges[0];
6418 else
6419 selectionRange = wxRICHTEXT_NO_SELECTION;
6420 }
6421 else
6422 selectionRange = wxRICHTEXT_NO_SELECTION;
6423
6424 int offset = GetRange().GetStart();
6425
6426 wxString str = m_text;
6427 if (context.HasVirtualText(this))
6428 {
6429 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6430 str = m_text;
6431 }
6432
6433 // Replace line break characters with spaces
6434 wxString toRemove = wxRichTextLineBreakChar;
6435 str.Replace(toRemove, wxT(" "));
6436 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6437 str.MakeUpper();
6438
6439 long len = range.GetLength();
6440 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
6441
6442 // Test for the optimized situations where all is selected, or none
6443 // is selected.
6444
6445 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6446 wxCheckSetFont(dc, textFont);
6447 int charHeight = dc.GetCharHeight();
6448
6449 int x, y;
6450 if ( textFont.IsOk() )
6451 {
6452 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6453 {
6454 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6455 wxCheckSetFont(dc, textFont);
6456 charHeight = dc.GetCharHeight();
6457 }
6458
6459 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6460 {
6461 if (textFont.IsUsingSizeInPixels())
6462 {
6463 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6464 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6465 x = rect.x;
6466 y = rect.y;
6467 }
6468 else
6469 {
6470 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6471 textFont.SetPointSize(static_cast<int>(size));
6472 x = rect.x;
6473 y = rect.y;
6474 }
6475 wxCheckSetFont(dc, textFont);
6476 }
6477 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6478 {
6479 if (textFont.IsUsingSizeInPixels())
6480 {
6481 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6482 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6483 x = rect.x;
6484 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6485 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6486 }
6487 else
6488 {
6489 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6490 textFont.SetPointSize(static_cast<int>(size));
6491 x = rect.x;
6492 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6493 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6494 }
6495 wxCheckSetFont(dc, textFont);
6496 }
6497 else
6498 {
6499 x = rect.x;
6500 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6501 }
6502 }
6503 else
6504 {
6505 x = rect.x;
6506 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6507 }
6508
6509 // TODO: new selection code
6510
6511 // (a) All selected.
6512 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
6513 {
6514 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
6515 }
6516 // (b) None selected.
6517 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6518 {
6519 // Draw all unselected
6520 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
6521 }
6522 else
6523 {
6524 // (c) Part selected, part not
6525 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6526
6527 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6528
6529 // 1. Initial unselected chunk, if any, up until start of selection.
6530 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6531 {
6532 int r1 = range.GetStart();
6533 int s1 = selectionRange.GetStart()-1;
6534 int fragmentLen = s1 - r1 + 1;
6535 if (fragmentLen < 0)
6536 {
6537 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
6538 }
6539 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
6540
6541 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6542
6543 #if USE_KERNING_FIX
6544 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6545 {
6546 // Compensate for kerning difference
6547 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6548 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
6549
6550 wxCoord w1, h1, w2, h2, w3, h3;
6551 dc.GetTextExtent(stringFragment, & w1, & h1);
6552 dc.GetTextExtent(stringFragment2, & w2, & h2);
6553 dc.GetTextExtent(stringFragment3, & w3, & h3);
6554
6555 int kerningDiff = (w1 + w3) - w2;
6556 x = x - kerningDiff;
6557 }
6558 #endif
6559 }
6560
6561 // 2. Selected chunk, if any.
6562 if (selectionRange.GetEnd() >= range.GetStart())
6563 {
6564 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6565 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6566
6567 int fragmentLen = s2 - s1 + 1;
6568 if (fragmentLen < 0)
6569 {
6570 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
6571 }
6572 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
6573
6574 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
6575
6576 #if USE_KERNING_FIX
6577 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6578 {
6579 // Compensate for kerning difference
6580 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6581 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
6582
6583 wxCoord w1, h1, w2, h2, w3, h3;
6584 dc.GetTextExtent(stringFragment, & w1, & h1);
6585 dc.GetTextExtent(stringFragment2, & w2, & h2);
6586 dc.GetTextExtent(stringFragment3, & w3, & h3);
6587
6588 int kerningDiff = (w1 + w3) - w2;
6589 x = x - kerningDiff;
6590 }
6591 #endif
6592 }
6593
6594 // 3. Remaining unselected chunk, if any
6595 if (selectionRange.GetEnd() < range.GetEnd())
6596 {
6597 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6598 int r2 = range.GetEnd();
6599
6600 int fragmentLen = r2 - s2 + 1;
6601 if (fragmentLen < 0)
6602 {
6603 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
6604 }
6605 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
6606
6607 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6608 }
6609 }
6610
6611 return true;
6612 }
6613
6614 bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
6615 {
6616 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6617
6618 wxArrayInt tabArray;
6619 int tabCount;
6620 if (hasTabs)
6621 {
6622 if (attr.GetTabs().IsEmpty())
6623 tabArray = wxRichTextParagraph::GetDefaultTabs();
6624 else
6625 tabArray = attr.GetTabs();
6626 tabCount = tabArray.GetCount();
6627
6628 for (int i = 0; i < tabCount; ++i)
6629 {
6630 int pos = tabArray[i];
6631 pos = ConvertTenthsMMToPixels(dc, pos);
6632 tabArray[i] = pos;
6633 }
6634 }
6635 else
6636 tabCount = 0;
6637
6638 int nextTabPos = -1;
6639 int tabPos = -1;
6640 wxCoord w, h;
6641
6642 if (selected)
6643 {
6644 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6645 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6646
6647 wxCheckSetBrush(dc, wxBrush(highlightColour));
6648 wxCheckSetPen(dc, wxPen(highlightColour));
6649 dc.SetTextForeground(highlightTextColour);
6650 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6651 }
6652 else
6653 {
6654 dc.SetTextForeground(attr.GetTextColour());
6655
6656 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6657 {
6658 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
6659 dc.SetTextBackground(attr.GetBackgroundColour());
6660 }
6661 else
6662 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6663 }
6664
6665 wxCoord x_orig = GetParent()->GetPosition().x;
6666 while (hasTabs)
6667 {
6668 // the string has a tab
6669 // break up the string at the Tab
6670 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6671 str = str.AfterFirst(wxT('\t'));
6672 dc.GetTextExtent(stringChunk, & w, & h);
6673 tabPos = x + w;
6674 bool not_found = true;
6675 for (int i = 0; i < tabCount && not_found; ++i)
6676 {
6677 nextTabPos = tabArray.Item(i) + x_orig;
6678
6679 // Find the next tab position.
6680 // Even if we're at the end of the tab array, we must still draw the chunk.
6681
6682 if (nextTabPos > tabPos || (i == (tabCount - 1)))
6683 {
6684 if (nextTabPos <= tabPos)
6685 {
6686 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6687 nextTabPos = tabPos + defaultTabWidth;
6688 }
6689
6690 not_found = false;
6691 if (selected)
6692 {
6693 w = nextTabPos - x;
6694 wxRect selRect(x, rect.y, w, rect.GetHeight());
6695 dc.DrawRectangle(selRect);
6696 }
6697 dc.DrawText(stringChunk, x, y);
6698
6699 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6700 {
6701 wxPen oldPen = dc.GetPen();
6702 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6703 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6704 wxCheckSetPen(dc, oldPen);
6705 }
6706
6707 x = nextTabPos;
6708 }
6709 }
6710 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6711 }
6712
6713 if (!str.IsEmpty())
6714 {
6715 dc.GetTextExtent(str, & w, & h);
6716 if (selected)
6717 {
6718 wxRect selRect(x, rect.y, w, rect.GetHeight());
6719 dc.DrawRectangle(selRect);
6720 }
6721 dc.DrawText(str, x, y);
6722
6723 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6724 {
6725 wxPen oldPen = dc.GetPen();
6726 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6727 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6728 wxCheckSetPen(dc, oldPen);
6729 }
6730
6731 x += w;
6732 }
6733
6734 return true;
6735 }
6736
6737 /// Lay the item out
6738 bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
6739 {
6740 // Only lay out if we haven't already cached the size
6741 if (m_size.x == -1)
6742 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
6743 m_maxSize = m_size;
6744 // Eventually we want to have a reasonable estimate of minimum size.
6745 m_minSize = wxSize(0, 0);
6746 return true;
6747 }
6748
6749 /// Copy
6750 void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6751 {
6752 wxRichTextObject::Copy(obj);
6753
6754 m_text = obj.m_text;
6755 }
6756
6757 /// Get/set the object size for the given range. Returns false if the range
6758 /// is invalid for this object.
6759 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), const wxPoint& position, const wxSize& WXUNUSED(parentSize), wxArrayInt* partialExtents) const
6760 {
6761 if (!range.IsWithin(GetRange()))
6762 return false;
6763
6764 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6765 wxASSERT (para != NULL);
6766
6767 int relativeX = position.x - GetParent()->GetPosition().x;
6768
6769 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6770 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
6771
6772 // Always assume unformatted text, since at this level we have no knowledge
6773 // of line breaks - and we don't need it, since we'll calculate size within
6774 // formatted text by doing it in chunks according to the line ranges
6775
6776 bool bScript(false);
6777 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
6778 if (font.IsOk())
6779 {
6780 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6781 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6782 {
6783 wxFont textFont = font;
6784 if (textFont.IsUsingSizeInPixels())
6785 {
6786 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6787 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6788 }
6789 else
6790 {
6791 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6792 textFont.SetPointSize(static_cast<int>(size));
6793 }
6794 wxCheckSetFont(dc, textFont);
6795 bScript = true;
6796 }
6797 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6798 {
6799 wxFont textFont = font;
6800 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6801 wxCheckSetFont(dc, textFont);
6802 bScript = true;
6803 }
6804 else
6805 {
6806 wxCheckSetFont(dc, font);
6807 }
6808 }
6809
6810 bool haveDescent = false;
6811 int startPos = range.GetStart() - GetRange().GetStart();
6812 long len = range.GetLength();
6813
6814 wxString str(m_text);
6815 if (context.HasVirtualText(this))
6816 {
6817 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6818 str = m_text;
6819 }
6820
6821 wxString toReplace = wxRichTextLineBreakChar;
6822 str.Replace(toReplace, wxT(" "));
6823
6824 wxString stringChunk = str.Mid(startPos, (size_t) len);
6825
6826 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6827 stringChunk.MakeUpper();
6828
6829 wxCoord w, h;
6830 int width = 0;
6831 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
6832 {
6833 // the string has a tab
6834 wxArrayInt tabArray;
6835 if (textAttr.GetTabs().IsEmpty())
6836 tabArray = wxRichTextParagraph::GetDefaultTabs();
6837 else
6838 tabArray = textAttr.GetTabs();
6839
6840 int tabCount = tabArray.GetCount();
6841
6842 for (int i = 0; i < tabCount; ++i)
6843 {
6844 int pos = tabArray[i];
6845 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6846 tabArray[i] = pos;
6847 }
6848
6849 int nextTabPos = -1;
6850
6851 while (stringChunk.Find(wxT('\t')) >= 0)
6852 {
6853 int absoluteWidth = 0;
6854
6855 // the string has a tab
6856 // break up the string at the Tab
6857 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6858 stringChunk = stringChunk.AfterFirst(wxT('\t'));
6859
6860 if (partialExtents)
6861 {
6862 int oldWidth;
6863 if (partialExtents->GetCount() > 0)
6864 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6865 else
6866 oldWidth = 0;
6867
6868 // Add these partial extents
6869 wxArrayInt p;
6870 dc.GetPartialTextExtents(stringFragment, p);
6871 size_t j;
6872 for (j = 0; j < p.GetCount(); j++)
6873 partialExtents->Add(oldWidth + p[j]);
6874
6875 if (partialExtents->GetCount() > 0)
6876 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
6877 else
6878 absoluteWidth = relativeX;
6879 }
6880 else
6881 {
6882 dc.GetTextExtent(stringFragment, & w, & h);
6883 width += w;
6884 absoluteWidth = width + relativeX;
6885 haveDescent = true;
6886 }
6887
6888 bool notFound = true;
6889 for (int i = 0; i < tabCount && notFound; ++i)
6890 {
6891 nextTabPos = tabArray.Item(i);
6892
6893 // Find the next tab position.
6894 // Even if we're at the end of the tab array, we must still process the chunk.
6895
6896 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
6897 {
6898 if (nextTabPos <= absoluteWidth)
6899 {
6900 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6901 nextTabPos = absoluteWidth + defaultTabWidth;
6902 }
6903
6904 notFound = false;
6905 width = nextTabPos - relativeX;
6906
6907 if (partialExtents)
6908 partialExtents->Add(width);
6909 }
6910 }
6911 }
6912 }
6913
6914 if (!stringChunk.IsEmpty())
6915 {
6916 if (partialExtents)
6917 {
6918 int oldWidth;
6919 if (partialExtents->GetCount() > 0)
6920 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6921 else
6922 oldWidth = 0;
6923
6924 // Add these partial extents
6925 wxArrayInt p;
6926 dc.GetPartialTextExtents(stringChunk, p);
6927 size_t j;
6928 for (j = 0; j < p.GetCount(); j++)
6929 partialExtents->Add(oldWidth + p[j]);
6930 }
6931 else
6932 {
6933 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6934 width += w;
6935 haveDescent = true;
6936 }
6937 }
6938
6939 if (partialExtents)
6940 {
6941 int charHeight = dc.GetCharHeight();
6942 if ((*partialExtents).GetCount() > 0)
6943 w = (*partialExtents)[partialExtents->GetCount()-1];
6944 else
6945 w = 0;
6946 size = wxSize(w, charHeight);
6947 }
6948 else
6949 {
6950 size = wxSize(width, dc.GetCharHeight());
6951 }
6952
6953 if (!haveDescent)
6954 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6955
6956 if ( bScript )
6957 dc.SetFont(font);
6958
6959 return true;
6960 }
6961
6962 /// Do a split, returning an object containing the second part, and setting
6963 /// the first part in 'this'.
6964 wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6965 {
6966 long index = pos - GetRange().GetStart();
6967
6968 if (index < 0 || index >= (int) m_text.length())
6969 return NULL;
6970
6971 wxString firstPart = m_text.Mid(0, index);
6972 wxString secondPart = m_text.Mid(index);
6973
6974 m_text = firstPart;
6975
6976 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6977 newObject->SetAttributes(GetAttributes());
6978 newObject->SetProperties(GetProperties());
6979
6980 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6981 GetRange().SetEnd(pos-1);
6982
6983 return newObject;
6984 }
6985
6986 /// Calculate range
6987 void wxRichTextPlainText::CalculateRange(long start, long& end)
6988 {
6989 end = start + m_text.length() - 1;
6990 m_range.SetRange(start, end);
6991 }
6992
6993 /// Delete range
6994 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6995 {
6996 wxRichTextRange r = range;
6997
6998 r.LimitTo(GetRange());
6999
7000 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
7001 {
7002 m_text.Empty();
7003 return true;
7004 }
7005
7006 long startIndex = r.GetStart() - GetRange().GetStart();
7007 long len = r.GetLength();
7008
7009 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
7010 return true;
7011 }
7012
7013 /// Get text for the given range.
7014 wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
7015 {
7016 wxRichTextRange r = range;
7017
7018 r.LimitTo(GetRange());
7019
7020 long startIndex = r.GetStart() - GetRange().GetStart();
7021 long len = r.GetLength();
7022
7023 return m_text.Mid(startIndex, len);
7024 }
7025
7026 /// Returns true if this object can merge itself with the given one.
7027 bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
7028 {
7029 // JACS 2013-01-27
7030 if (!context.GetVirtualAttributesEnabled())
7031 {
7032 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
7033 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
7034 }
7035 else
7036 {
7037 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7038 if (!otherObj || m_text.empty())
7039 return false;
7040
7041 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7042 return false;
7043
7044 // Check if differing virtual attributes makes it impossible to merge
7045 // these strings.
7046
7047 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7048 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7049 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7050 return true;
7051 else if (hasVirtualAttr1 != hasVirtualAttr2)
7052 return false;
7053 else
7054 {
7055 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7056 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7057 return virtualAttr1 == virtualAttr2;
7058 }
7059 }
7060 }
7061
7062 /// Returns true if this object merged itself with the given one.
7063 /// The calling code will then delete the given object.
7064 bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
7065 {
7066 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7067 wxASSERT( textObject != NULL );
7068
7069 if (textObject)
7070 {
7071 m_text += textObject->GetText();
7072 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
7073 return true;
7074 }
7075 else
7076 return false;
7077 }
7078
7079 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7080 {
7081 // If this object has any virtual attributes at all, whether for the whole object
7082 // or individual ones, we should try splitting it by calling Split.
7083 // Must be more than one character in order to be able to split.
7084 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7085 }
7086
7087 wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7088 {
7089 int count = context.GetVirtualSubobjectAttributesCount(this);
7090 if (count > 0 && GetParent())
7091 {
7092 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7093 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7094 if (node)
7095 {
7096 const wxRichTextAttr emptyAttr;
7097 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7098
7099 wxArrayInt positions;
7100 wxRichTextAttrArray attributes;
7101 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7102 {
7103 wxASSERT(positions.GetCount() == attributes.GetCount());
7104
7105 // We will gather up runs of text with the same virtual attributes
7106
7107 int len = m_text.Length();
7108 int i = 0;
7109
7110 // runStart and runEnd represent the accumulated run with a consistent attribute
7111 // that hasn't yet been appended
7112 int runStart = -1;
7113 int runEnd = -1;
7114 wxRichTextAttr currentAttr;
7115 wxString text = m_text;
7116 wxRichTextPlainText* lastPlainText = this;
7117
7118 for (i = 0; i < (int) positions.GetCount(); i++)
7119 {
7120 int pos = positions[i];
7121 wxASSERT(pos >= 0 && pos < len);
7122 if (pos >= 0 && pos < len)
7123 {
7124 const wxRichTextAttr& attr = attributes[i];
7125
7126 if (pos == 0)
7127 {
7128 runStart = 0;
7129 currentAttr = attr;
7130 }
7131 // Check if there was a gap from the last known attribute and this.
7132 // In that case, we need to do something with the span of non-attributed text.
7133 else if ((pos-1) > runEnd)
7134 {
7135 if (runEnd == -1)
7136 {
7137 // We hadn't processed anything previously, so the previous run is from the text start
7138 // to just before this position. The current attribute remains empty.
7139 runStart = 0;
7140 runEnd = pos-1;
7141 }
7142 else
7143 {
7144 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7145 // then just extend the run.
7146 if (currentAttr.IsDefault())
7147 {
7148 runEnd = pos-1;
7149 }
7150 else
7151 {
7152 // We need to add an object, or reuse the existing one.
7153 if (runStart == 0)
7154 {
7155 lastPlainText = this;
7156 SetText(text.Mid(runStart, runEnd - runStart + 1));
7157 }
7158 else
7159 {
7160 wxRichTextPlainText* obj = new wxRichTextPlainText;
7161 lastPlainText = obj;
7162 obj->SetAttributes(GetAttributes());
7163 obj->SetProperties(GetProperties());
7164 obj->SetParent(parent);
7165
7166 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7167 if (next)
7168 parent->GetChildren().Insert(next, obj);
7169 else
7170 parent->GetChildren().Append(obj);
7171 }
7172
7173 runStart = runEnd+1;
7174 runEnd = pos-1;
7175
7176 currentAttr = emptyAttr;
7177 }
7178 }
7179 }
7180
7181 wxASSERT(runEnd == pos-1);
7182
7183 // Now we only have to deal with the previous run
7184 if (currentAttr == attr)
7185 {
7186 // If we still have the same attributes, then we
7187 // simply increase the run size.
7188 runEnd = pos;
7189 }
7190 else
7191 {
7192 if (runEnd >= 0)
7193 {
7194 // We need to add an object, or reuse the existing one.
7195 if (runStart == 0)
7196 {
7197 lastPlainText = this;
7198 SetText(text.Mid(runStart, runEnd - runStart + 1));
7199 }
7200 else
7201 {
7202 wxRichTextPlainText* obj = new wxRichTextPlainText;
7203 lastPlainText = obj;
7204 obj->SetAttributes(GetAttributes());
7205 obj->SetProperties(GetProperties());
7206 obj->SetParent(parent);
7207
7208 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7209 if (next)
7210 parent->GetChildren().Insert(next, obj);
7211 else
7212 parent->GetChildren().Append(obj);
7213 }
7214 }
7215
7216 runStart = pos;
7217 runEnd = pos;
7218
7219 currentAttr = attr;
7220 }
7221 }
7222 }
7223
7224 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7225 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7226 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7227 {
7228 // If the current attribute is empty, merge the run with the next fragment
7229 // which by definition (because it's not specified) has empty attributes.
7230 if (currentAttr.IsDefault())
7231 runEnd = (len-1);
7232
7233 if (runEnd < (len-1))
7234 {
7235 // We need to add an object, or reuse the existing one.
7236 if (runStart == 0)
7237 {
7238 lastPlainText = this;
7239 SetText(text.Mid(runStart, runEnd - runStart + 1));
7240 }
7241 else
7242 {
7243 wxRichTextPlainText* obj = new wxRichTextPlainText;
7244 lastPlainText = obj;
7245 obj->SetAttributes(GetAttributes());
7246 obj->SetProperties(GetProperties());
7247 obj->SetParent(parent);
7248
7249 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7250 if (next)
7251 parent->GetChildren().Insert(next, obj);
7252 else
7253 parent->GetChildren().Append(obj);
7254 }
7255
7256 runStart = runEnd+1;
7257 runEnd = (len-1);
7258 }
7259
7260 // Now the last, non-attributed fragment at the end, if any
7261 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7262 {
7263 wxASSERT(runStart != 0);
7264
7265 wxRichTextPlainText* obj = new wxRichTextPlainText;
7266 obj->SetAttributes(GetAttributes());
7267 obj->SetProperties(GetProperties());
7268 obj->SetParent(parent);
7269
7270 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7271 if (next)
7272 parent->GetChildren().Insert(next, obj);
7273 else
7274 parent->GetChildren().Append(obj);
7275
7276 lastPlainText = obj;
7277 }
7278 }
7279
7280 return lastPlainText;
7281 }
7282 }
7283 }
7284 return this;
7285 }
7286
7287 /// Dump to output stream for debugging
7288 void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7289 {
7290 wxRichTextObject::Dump(stream);
7291 stream << m_text << wxT("\n");
7292 }
7293
7294 /// Get the first position from pos that has a line break character.
7295 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7296 {
7297 int i;
7298 int len = m_text.length();
7299 int startPos = pos - m_range.GetStart();
7300 for (i = startPos; i < len; i++)
7301 {
7302 wxChar ch = m_text[i];
7303 if (ch == wxRichTextLineBreakChar)
7304 {
7305 return i + m_range.GetStart();
7306 }
7307 }
7308 return -1;
7309 }
7310
7311 /*!
7312 * wxRichTextBuffer
7313 * This is a kind of box, used to represent the whole buffer
7314 */
7315
7316 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7317
7318 wxList wxRichTextBuffer::sm_handlers;
7319 wxList wxRichTextBuffer::sm_drawingHandlers;
7320 wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7321 wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7322 int wxRichTextBuffer::sm_bulletRightMargin = 20;
7323 float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
7324 bool wxRichTextBuffer::sm_floatingLayoutMode = true;
7325
7326 /// Initialisation
7327 void wxRichTextBuffer::Init()
7328 {
7329 m_commandProcessor = new wxCommandProcessor;
7330 m_styleSheet = NULL;
7331 m_modified = false;
7332 m_batchedCommandDepth = 0;
7333 m_batchedCommand = NULL;
7334 m_suppressUndo = 0;
7335 m_handlerFlags = 0;
7336 m_scale = 1.0;
7337 m_dimensionScale = 1.0;
7338 m_fontScale = 1.0;
7339 SetMargins(4);
7340 }
7341
7342 /// Initialisation
7343 wxRichTextBuffer::~wxRichTextBuffer()
7344 {
7345 delete m_commandProcessor;
7346 delete m_batchedCommand;
7347
7348 ClearStyleStack();
7349 ClearEventHandlers();
7350 }
7351
7352 void wxRichTextBuffer::ResetAndClearCommands()
7353 {
7354 Reset();
7355
7356 GetCommandProcessor()->ClearCommands();
7357
7358 Modify(false);
7359 Invalidate(wxRICHTEXT_ALL);
7360 }
7361
7362 void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7363 {
7364 wxRichTextParagraphLayoutBox::Copy(obj);
7365
7366 m_styleSheet = obj.m_styleSheet;
7367 m_modified = obj.m_modified;
7368 m_batchedCommandDepth = 0;
7369 if (m_batchedCommand)
7370 delete m_batchedCommand;
7371 m_batchedCommand = NULL;
7372 m_suppressUndo = obj.m_suppressUndo;
7373 m_invalidRange = obj.m_invalidRange;
7374 m_dimensionScale = obj.m_dimensionScale;
7375 m_fontScale = obj.m_fontScale;
7376 }
7377
7378 /// Push style sheet to top of stack
7379 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7380 {
7381 if (m_styleSheet)
7382 styleSheet->InsertSheet(m_styleSheet);
7383
7384 SetStyleSheet(styleSheet);
7385
7386 return true;
7387 }
7388
7389 /// Pop style sheet from top of stack
7390 wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7391 {
7392 if (m_styleSheet)
7393 {
7394 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7395 m_styleSheet = oldSheet->GetNextSheet();
7396 oldSheet->Unlink();
7397
7398 return oldSheet;
7399 }
7400 else
7401 return NULL;
7402 }
7403
7404 /// Submit command to insert paragraphs
7405 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7406 {
7407 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
7408 }
7409
7410 /// Submit command to insert paragraphs
7411 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
7412 {
7413 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7414
7415 action->GetNewParagraphs() = paragraphs;
7416
7417 action->SetPosition(pos);
7418
7419 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
7420 if (!paragraphs.GetPartialParagraph())
7421 range.SetEnd(range.GetEnd()+1);
7422
7423 // Set the range we'll need to delete in Undo
7424 action->SetRange(range);
7425
7426 buffer->SubmitAction(action);
7427
7428 return true;
7429 }
7430
7431 /// Submit command to insert the given text
7432 bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7433 {
7434 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
7435 }
7436
7437 /// Submit command to insert the given text
7438 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7439 {
7440 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7441
7442 wxRichTextAttr* p = NULL;
7443 wxRichTextAttr paraAttr;
7444 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7445 {
7446 // Get appropriate paragraph style
7447 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
7448 if (!paraAttr.IsDefault())
7449 p = & paraAttr;
7450 }
7451
7452 action->GetNewParagraphs().AddParagraphs(text, p);
7453
7454 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
7455
7456 if (!text.empty() && text.Last() != wxT('\n'))
7457 {
7458 // Don't count the newline when undoing
7459 length --;
7460 action->GetNewParagraphs().SetPartialParagraph(true);
7461 }
7462 else if (!text.empty() && text.Last() == wxT('\n'))
7463 length --;
7464
7465 action->SetPosition(pos);
7466
7467 // Set the range we'll need to delete in Undo
7468 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7469
7470 buffer->SubmitAction(action);
7471
7472 return true;
7473 }
7474
7475 /// Submit command to insert the given text
7476 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
7477 {
7478 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
7479 }
7480
7481 /// Submit command to insert the given text
7482 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
7483 {
7484 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7485
7486 wxRichTextAttr* p = NULL;
7487 wxRichTextAttr paraAttr;
7488 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7489 {
7490 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
7491 if (!paraAttr.IsDefault())
7492 p = & paraAttr;
7493 }
7494
7495 wxRichTextAttr attr(buffer->GetDefaultStyle());
7496 // Don't include box attributes such as margins
7497 attr.GetTextBoxAttr().Reset();
7498
7499 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
7500 action->GetNewParagraphs().AppendChild(newPara);
7501 action->GetNewParagraphs().UpdateRanges();
7502 action->GetNewParagraphs().SetPartialParagraph(false);
7503 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7504 long pos1 = pos;
7505
7506 if (p)
7507 newPara->SetAttributes(*p);
7508
7509 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7510 {
7511 if (para && para->GetRange().GetEnd() == pos)
7512 pos1 ++;
7513
7514 // Now see if we need to number the paragraph.
7515 if (newPara->GetAttributes().HasBulletNumber())
7516 {
7517 wxRichTextAttr numberingAttr;
7518 if (FindNextParagraphNumber(para, numberingAttr))
7519 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7520 }
7521 }
7522
7523 action->SetPosition(pos);
7524
7525 // Use the default character style
7526 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
7527 {
7528 // Check whether the default style merely reflects the paragraph/basic style,
7529 // in which case don't apply it.
7530 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
7531 defaultStyle.GetTextBoxAttr().Reset();
7532 wxRichTextAttr toApply;
7533 if (para)
7534 {
7535 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
7536 wxRichTextAttr newAttr;
7537 // This filters out attributes that are accounted for by the current
7538 // paragraph/basic style
7539 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7540 }
7541 else
7542 toApply = defaultStyle;
7543
7544 if (!toApply.IsDefault())
7545 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7546 }
7547
7548 // Set the range we'll need to delete in Undo
7549 action->SetRange(wxRichTextRange(pos1, pos1));
7550
7551 buffer->SubmitAction(action);
7552
7553 return true;
7554 }
7555
7556 /// Submit command to insert the given image
7557 bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7558 const wxRichTextAttr& textAttr)
7559 {
7560 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
7561 }
7562
7563 /// Submit command to insert the given image
7564 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7565 wxRichTextCtrl* ctrl, int flags,
7566 const wxRichTextAttr& textAttr)
7567 {
7568 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7569
7570 wxRichTextAttr* p = NULL;
7571 wxRichTextAttr paraAttr;
7572 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7573 {
7574 paraAttr = GetStyleForNewParagraph(buffer, pos);
7575 if (!paraAttr.IsDefault())
7576 p = & paraAttr;
7577 }
7578
7579 wxRichTextAttr attr(buffer->GetDefaultStyle());
7580
7581 // Don't include box attributes such as margins
7582 attr.GetTextBoxAttr().Reset();
7583
7584 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7585 if (p)
7586 newPara->SetAttributes(*p);
7587
7588 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7589 newPara->AppendChild(imageObject);
7590 imageObject->SetAttributes(textAttr);
7591 action->GetNewParagraphs().AppendChild(newPara);
7592 action->GetNewParagraphs().UpdateRanges();
7593
7594 action->GetNewParagraphs().SetPartialParagraph(true);
7595
7596 action->SetPosition(pos);
7597
7598 // Set the range we'll need to delete in Undo
7599 action->SetRange(wxRichTextRange(pos, pos));
7600
7601 buffer->SubmitAction(action);
7602
7603 return true;
7604 }
7605
7606 // Insert an object with no change of it
7607 wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7608 {
7609 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
7610 }
7611
7612 // Insert an object with no change of it
7613 wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7614 {
7615 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7616
7617 wxRichTextAttr* p = NULL;
7618 wxRichTextAttr paraAttr;
7619 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7620 {
7621 paraAttr = GetStyleForNewParagraph(buffer, pos);
7622 if (!paraAttr.IsDefault())
7623 p = & paraAttr;
7624 }
7625
7626 wxRichTextAttr attr(buffer->GetDefaultStyle());
7627
7628 // Don't include box attributes such as margins
7629 attr.GetTextBoxAttr().Reset();
7630
7631 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7632 if (p)
7633 newPara->SetAttributes(*p);
7634
7635 newPara->AppendChild(object);
7636 action->GetNewParagraphs().AppendChild(newPara);
7637 action->GetNewParagraphs().UpdateRanges();
7638
7639 action->GetNewParagraphs().SetPartialParagraph(true);
7640
7641 action->SetPosition(pos);
7642
7643 // Set the range we'll need to delete in Undo
7644 action->SetRange(wxRichTextRange(pos, pos));
7645
7646 buffer->SubmitAction(action);
7647
7648 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7649 return obj;
7650 }
7651
7652 wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7653 const wxRichTextProperties& properties,
7654 wxRichTextCtrl* ctrl, int flags,
7655 const wxRichTextAttr& textAttr)
7656 {
7657 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7658
7659 wxRichTextAttr* p = NULL;
7660 wxRichTextAttr paraAttr;
7661 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7662 {
7663 paraAttr = GetStyleForNewParagraph(buffer, pos);
7664 if (!paraAttr.IsDefault())
7665 p = & paraAttr;
7666 }
7667
7668 wxRichTextAttr attr(buffer->GetDefaultStyle());
7669
7670 // Don't include box attributes such as margins
7671 attr.GetTextBoxAttr().Reset();
7672
7673 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7674 if (p)
7675 newPara->SetAttributes(*p);
7676
7677 wxRichTextField* fieldObject = new wxRichTextField();
7678 fieldObject->wxRichTextObject::SetProperties(properties);
7679 fieldObject->SetFieldType(fieldType);
7680 fieldObject->SetAttributes(textAttr);
7681 newPara->AppendChild(fieldObject);
7682 action->GetNewParagraphs().AppendChild(newPara);
7683 action->GetNewParagraphs().UpdateRanges();
7684 action->GetNewParagraphs().SetPartialParagraph(true);
7685 action->SetPosition(pos);
7686
7687 // Set the range we'll need to delete in Undo
7688 action->SetRange(wxRichTextRange(pos, pos));
7689
7690 buffer->SubmitAction(action);
7691
7692 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7693 return obj;
7694 }
7695
7696 /// Get the style that is appropriate for a new paragraph at this position.
7697 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7698 /// style.
7699 wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
7700 {
7701 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7702 if (para)
7703 {
7704 wxRichTextAttr attr;
7705 bool foundAttributes = false;
7706
7707 // Look for a matching paragraph style
7708 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
7709 {
7710 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
7711 if (paraDef)
7712 {
7713 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7714 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
7715 {
7716 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
7717 if (nextParaDef)
7718 {
7719 foundAttributes = true;
7720 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7721 }
7722 }
7723
7724 // If we didn't find the 'next style', use this style instead.
7725 if (!foundAttributes)
7726 {
7727 foundAttributes = true;
7728 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7729 }
7730 }
7731 }
7732
7733 // Also apply list style if present
7734 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
7735 {
7736 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
7737 if (listDef)
7738 {
7739 int thisIndent = para->GetAttributes().GetLeftIndent();
7740 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7741
7742 // Apply the overall list style, and item style for this level
7743 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
7744 wxRichTextApplyStyle(attr, listStyle);
7745 attr.SetOutlineLevel(thisLevel);
7746 if (para->GetAttributes().HasBulletNumber())
7747 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7748 }
7749 }
7750
7751 if (!foundAttributes)
7752 {
7753 attr = para->GetAttributes();
7754 int flags = attr.GetFlags();
7755
7756 // Eliminate character styles
7757 flags &= ( (~ wxTEXT_ATTR_FONT) |
7758 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7759 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
7760 attr.SetFlags(flags);
7761 }
7762
7763 return attr;
7764 }
7765 else
7766 return wxRichTextAttr();
7767 }
7768
7769 /// Submit command to delete this range
7770 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
7771 {
7772 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7773 }
7774
7775 /// Submit command to delete this range
7776 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7777 {
7778 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7779
7780 action->SetPosition(ctrl->GetCaretPosition());
7781
7782 // Set the range to delete
7783 action->SetRange(range);
7784
7785 // Copy the fragment that we'll need to restore in Undo
7786 CopyFragment(range, action->GetOldParagraphs());
7787
7788 // See if we're deleting a paragraph marker, in which case we need to
7789 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7790 if (range.GetStart() == range.GetEnd())
7791 {
7792 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7793 if (para && para->GetRange().GetEnd() == range.GetEnd())
7794 {
7795 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7796 if (nextPara && nextPara != para)
7797 {
7798 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7799 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
7800 }
7801 }
7802 }
7803
7804 buffer->SubmitAction(action);
7805
7806 return true;
7807 }
7808
7809 /// Collapse undo/redo commands
7810 bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7811 {
7812 if (m_batchedCommandDepth == 0)
7813 {
7814 wxASSERT(m_batchedCommand == NULL);
7815 if (m_batchedCommand)
7816 {
7817 GetCommandProcessor()->Store(m_batchedCommand);
7818 }
7819 m_batchedCommand = new wxRichTextCommand(cmdName);
7820 }
7821
7822 m_batchedCommandDepth ++;
7823
7824 return true;
7825 }
7826
7827 /// Collapse undo/redo commands
7828 bool wxRichTextBuffer::EndBatchUndo()
7829 {
7830 m_batchedCommandDepth --;
7831
7832 wxASSERT(m_batchedCommandDepth >= 0);
7833 wxASSERT(m_batchedCommand != NULL);
7834
7835 if (m_batchedCommandDepth == 0)
7836 {
7837 GetCommandProcessor()->Store(m_batchedCommand);
7838 m_batchedCommand = NULL;
7839 }
7840
7841 return true;
7842 }
7843
7844 /// Submit immediately, or delay according to whether collapsing is on
7845 bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7846 {
7847 if (action && !action->GetNewParagraphs().IsEmpty())
7848 PrepareContent(action->GetNewParagraphs());
7849
7850 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
7851 {
7852 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7853 cmd->AddAction(action);
7854 cmd->Do();
7855 cmd->GetActions().Clear();
7856 delete cmd;
7857
7858 m_batchedCommand->AddAction(action);
7859 }
7860 else
7861 {
7862 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7863 cmd->AddAction(action);
7864
7865 // Only store it if we're not suppressing undo.
7866 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7867 }
7868
7869 return true;
7870 }
7871
7872 /// Begin suppressing undo/redo commands.
7873 bool wxRichTextBuffer::BeginSuppressUndo()
7874 {
7875 m_suppressUndo ++;
7876
7877 return true;
7878 }
7879
7880 /// End suppressing undo/redo commands.
7881 bool wxRichTextBuffer::EndSuppressUndo()
7882 {
7883 m_suppressUndo --;
7884
7885 return true;
7886 }
7887
7888 /// Begin using a style
7889 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
7890 {
7891 wxRichTextAttr newStyle(GetDefaultStyle());
7892 newStyle.GetTextBoxAttr().Reset();
7893
7894 // Save the old default style
7895 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
7896
7897 wxRichTextApplyStyle(newStyle, style);
7898 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7899
7900 SetDefaultStyle(newStyle);
7901
7902 return true;
7903 }
7904
7905 /// End the style
7906 bool wxRichTextBuffer::EndStyle()
7907 {
7908 if (!m_attributeStack.GetFirst())
7909 {
7910 wxLogDebug(_("Too many EndStyle calls!"));
7911 return false;
7912 }
7913
7914 wxList::compatibility_iterator node = m_attributeStack.GetLast();
7915 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
7916 m_attributeStack.Erase(node);
7917
7918 SetDefaultStyle(*attr);
7919
7920 delete attr;
7921 return true;
7922 }
7923
7924 /// End all styles
7925 bool wxRichTextBuffer::EndAllStyles()
7926 {
7927 while (m_attributeStack.GetCount() != 0)
7928 EndStyle();
7929 return true;
7930 }
7931
7932 /// Clear the style stack
7933 void wxRichTextBuffer::ClearStyleStack()
7934 {
7935 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
7936 delete (wxRichTextAttr*) node->GetData();
7937 m_attributeStack.Clear();
7938 }
7939
7940 /// Begin using bold
7941 bool wxRichTextBuffer::BeginBold()
7942 {
7943 wxRichTextAttr attr;
7944 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7945
7946 return BeginStyle(attr);
7947 }
7948
7949 /// Begin using italic
7950 bool wxRichTextBuffer::BeginItalic()
7951 {
7952 wxRichTextAttr attr;
7953 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7954
7955 return BeginStyle(attr);
7956 }
7957
7958 /// Begin using underline
7959 bool wxRichTextBuffer::BeginUnderline()
7960 {
7961 wxRichTextAttr attr;
7962 attr.SetFontUnderlined(true);
7963
7964 return BeginStyle(attr);
7965 }
7966
7967 /// Begin using point size
7968 bool wxRichTextBuffer::BeginFontSize(int pointSize)
7969 {
7970 wxRichTextAttr attr;
7971 attr.SetFontSize(pointSize);
7972
7973 return BeginStyle(attr);
7974 }
7975
7976 /// Begin using this font
7977 bool wxRichTextBuffer::BeginFont(const wxFont& font)
7978 {
7979 wxRichTextAttr attr;
7980 attr.SetFont(font);
7981
7982 return BeginStyle(attr);
7983 }
7984
7985 /// Begin using this colour
7986 bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7987 {
7988 wxRichTextAttr attr;
7989 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7990 attr.SetTextColour(colour);
7991
7992 return BeginStyle(attr);
7993 }
7994
7995 /// Begin using alignment
7996 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7997 {
7998 wxRichTextAttr attr;
7999 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
8000 attr.SetAlignment(alignment);
8001
8002 return BeginStyle(attr);
8003 }
8004
8005 /// Begin left indent
8006 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
8007 {
8008 wxRichTextAttr attr;
8009 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
8010 attr.SetLeftIndent(leftIndent, leftSubIndent);
8011
8012 return BeginStyle(attr);
8013 }
8014
8015 /// Begin right indent
8016 bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
8017 {
8018 wxRichTextAttr attr;
8019 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
8020 attr.SetRightIndent(rightIndent);
8021
8022 return BeginStyle(attr);
8023 }
8024
8025 /// Begin paragraph spacing
8026 bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
8027 {
8028 long flags = 0;
8029 if (before != 0)
8030 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
8031 if (after != 0)
8032 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
8033
8034 wxRichTextAttr attr;
8035 attr.SetFlags(flags);
8036 attr.SetParagraphSpacingBefore(before);
8037 attr.SetParagraphSpacingAfter(after);
8038
8039 return BeginStyle(attr);
8040 }
8041
8042 /// Begin line spacing
8043 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8044 {
8045 wxRichTextAttr attr;
8046 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8047 attr.SetLineSpacing(lineSpacing);
8048
8049 return BeginStyle(attr);
8050 }
8051
8052 /// Begin numbered bullet
8053 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8054 {
8055 wxRichTextAttr attr;
8056 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8057 attr.SetBulletStyle(bulletStyle);
8058 attr.SetBulletNumber(bulletNumber);
8059 attr.SetLeftIndent(leftIndent, leftSubIndent);
8060
8061 return BeginStyle(attr);
8062 }
8063
8064 /// Begin symbol bullet
8065 bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
8066 {
8067 wxRichTextAttr attr;
8068 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8069 attr.SetBulletStyle(bulletStyle);
8070 attr.SetLeftIndent(leftIndent, leftSubIndent);
8071 attr.SetBulletText(symbol);
8072
8073 return BeginStyle(attr);
8074 }
8075
8076 /// Begin standard bullet
8077 bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8078 {
8079 wxRichTextAttr attr;
8080 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8081 attr.SetBulletStyle(bulletStyle);
8082 attr.SetLeftIndent(leftIndent, leftSubIndent);
8083 attr.SetBulletName(bulletName);
8084
8085 return BeginStyle(attr);
8086 }
8087
8088 /// Begin named character style
8089 bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8090 {
8091 if (GetStyleSheet())
8092 {
8093 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8094 if (def)
8095 {
8096 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8097 return BeginStyle(attr);
8098 }
8099 }
8100 return false;
8101 }
8102
8103 /// Begin named paragraph style
8104 bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8105 {
8106 if (GetStyleSheet())
8107 {
8108 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8109 if (def)
8110 {
8111 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8112 return BeginStyle(attr);
8113 }
8114 }
8115 return false;
8116 }
8117
8118 /// Begin named list style
8119 bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8120 {
8121 if (GetStyleSheet())
8122 {
8123 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8124 if (def)
8125 {
8126 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
8127
8128 attr.SetBulletNumber(number);
8129
8130 return BeginStyle(attr);
8131 }
8132 }
8133 return false;
8134 }
8135
8136 /// Begin URL
8137 bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8138 {
8139 wxRichTextAttr attr;
8140
8141 if (!characterStyle.IsEmpty() && GetStyleSheet())
8142 {
8143 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8144 if (def)
8145 {
8146 attr = def->GetStyleMergedWithBase(GetStyleSheet());
8147 }
8148 }
8149 attr.SetURL(url);
8150
8151 return BeginStyle(attr);
8152 }
8153
8154 /// Adds a handler to the end
8155 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8156 {
8157 sm_handlers.Append(handler);
8158 }
8159
8160 /// Inserts a handler at the front
8161 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8162 {
8163 sm_handlers.Insert( handler );
8164 }
8165
8166 /// Removes a handler
8167 bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8168 {
8169 wxRichTextFileHandler *handler = FindHandler(name);
8170 if (handler)
8171 {
8172 sm_handlers.DeleteObject(handler);
8173 delete handler;
8174 return true;
8175 }
8176 else
8177 return false;
8178 }
8179
8180 /// Finds a handler by filename or, if supplied, type
8181 wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8182 wxRichTextFileType imageType)
8183 {
8184 if (imageType != wxRICHTEXT_TYPE_ANY)
8185 return FindHandler(imageType);
8186 else if (!filename.IsEmpty())
8187 {
8188 wxString path, file, ext;
8189 wxFileName::SplitPath(filename, & path, & file, & ext);
8190 return FindHandler(ext, imageType);
8191 }
8192 else
8193 return NULL;
8194 }
8195
8196
8197 /// Finds a handler by name
8198 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8199 {
8200 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8201 while (node)
8202 {
8203 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8204 if (handler->GetName().Lower() == name.Lower()) return handler;
8205
8206 node = node->GetNext();
8207 }
8208 return NULL;
8209 }
8210
8211 /// Finds a handler by extension and type
8212 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
8213 {
8214 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8215 while (node)
8216 {
8217 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8218 if ( handler->GetExtension().Lower() == extension.Lower() &&
8219 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8220 return handler;
8221 node = node->GetNext();
8222 }
8223 return 0;
8224 }
8225
8226 /// Finds a handler by type
8227 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
8228 {
8229 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8230 while (node)
8231 {
8232 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8233 if (handler->GetType() == type) return handler;
8234 node = node->GetNext();
8235 }
8236 return NULL;
8237 }
8238
8239 void wxRichTextBuffer::InitStandardHandlers()
8240 {
8241 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8242 AddHandler(new wxRichTextPlainTextHandler);
8243 }
8244
8245 void wxRichTextBuffer::CleanUpHandlers()
8246 {
8247 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8248 while (node)
8249 {
8250 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8251 wxList::compatibility_iterator next = node->GetNext();
8252 delete handler;
8253 node = next;
8254 }
8255
8256 sm_handlers.Clear();
8257 }
8258
8259 wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
8260 {
8261 if (types)
8262 types->Clear();
8263
8264 wxString wildcard;
8265
8266 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8267 int count = 0;
8268 while (node)
8269 {
8270 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
8271 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
8272 {
8273 if (combine)
8274 {
8275 if (count > 0)
8276 wildcard += wxT(";");
8277 wildcard += wxT("*.") + handler->GetExtension();
8278 }
8279 else
8280 {
8281 if (count > 0)
8282 wildcard += wxT("|");
8283 wildcard += handler->GetName();
8284 wildcard += wxT(" ");
8285 wildcard += _("files");
8286 wildcard += wxT(" (*.");
8287 wildcard += handler->GetExtension();
8288 wildcard += wxT(")|*.");
8289 wildcard += handler->GetExtension();
8290 if (types)
8291 types->Add(handler->GetType());
8292 }
8293 count ++;
8294 }
8295
8296 node = node->GetNext();
8297 }
8298
8299 if (combine)
8300 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8301 return wildcard;
8302 }
8303
8304 /// Load a file
8305 bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
8306 {
8307 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8308 if (handler)
8309 {
8310 SetDefaultStyle(wxRichTextAttr());
8311 handler->SetFlags(GetHandlerFlags());
8312 bool success = handler->LoadFile(this, filename);
8313 Invalidate(wxRICHTEXT_ALL);
8314 return success;
8315 }
8316 else
8317 return false;
8318 }
8319
8320 /// Save a file
8321 bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
8322 {
8323 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8324 if (handler)
8325 {
8326 handler->SetFlags(GetHandlerFlags());
8327 return handler->SaveFile(this, filename);
8328 }
8329 else
8330 return false;
8331 }
8332
8333 /// Load from a stream
8334 bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
8335 {
8336 wxRichTextFileHandler* handler = FindHandler(type);
8337 if (handler)
8338 {
8339 SetDefaultStyle(wxRichTextAttr());
8340 handler->SetFlags(GetHandlerFlags());
8341 bool success = handler->LoadFile(this, stream);
8342 Invalidate(wxRICHTEXT_ALL);
8343 return success;
8344 }
8345 else
8346 return false;
8347 }
8348
8349 /// Save to a stream
8350 bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
8351 {
8352 wxRichTextFileHandler* handler = FindHandler(type);
8353 if (handler)
8354 {
8355 handler->SetFlags(GetHandlerFlags());
8356 return handler->SaveFile(this, stream);
8357 }
8358 else
8359 return false;
8360 }
8361
8362 /// Copy the range to the clipboard
8363 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8364 {
8365 bool success = false;
8366 wxRichTextParagraphLayoutBox* container = this;
8367 if (GetRichTextCtrl())
8368 container = GetRichTextCtrl()->GetFocusObject();
8369
8370 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8371
8372 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8373 {
8374 wxTheClipboard->Clear();
8375
8376 // Add composite object
8377
8378 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8379
8380 {
8381 wxString text = container->GetTextForRange(range);
8382
8383 #ifdef __WXMSW__
8384 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8385 #endif
8386
8387 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8388 }
8389
8390 // Add rich text buffer data object. This needs the XML handler to be present.
8391
8392 if (FindHandler(wxRICHTEXT_TYPE_XML))
8393 {
8394 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
8395 container->CopyFragment(range, *richTextBuf);
8396
8397 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8398 }
8399
8400 if (wxTheClipboard->SetData(compositeObject))
8401 success = true;
8402
8403 wxTheClipboard->Close();
8404 }
8405
8406 #else
8407 wxUnusedVar(range);
8408 #endif
8409 return success;
8410 }
8411
8412 /// Paste the clipboard content to the buffer
8413 bool wxRichTextBuffer::PasteFromClipboard(long position)
8414 {
8415 bool success = false;
8416 wxRichTextParagraphLayoutBox* container = this;
8417 if (GetRichTextCtrl())
8418 container = GetRichTextCtrl()->GetFocusObject();
8419
8420 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8421 if (CanPasteFromClipboard())
8422 {
8423 if (wxTheClipboard->Open())
8424 {
8425 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8426 {
8427 wxRichTextBufferDataObject data;
8428 wxTheClipboard->GetData(data);
8429 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8430 if (richTextBuffer)
8431 {
8432 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
8433 if (GetRichTextCtrl())
8434 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
8435 delete richTextBuffer;
8436 }
8437 }
8438 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
8439 #if wxUSE_UNICODE
8440 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8441 #endif
8442 )
8443 {
8444 wxTextDataObject data;
8445 wxTheClipboard->GetData(data);
8446 wxString text(data.GetText());
8447 #ifdef __WXMSW__
8448 wxString text2;
8449 text2.Alloc(text.Length()+1);
8450 size_t i;
8451 for (i = 0; i < text.Length(); i++)
8452 {
8453 wxChar ch = text[i];
8454 if (ch != wxT('\r'))
8455 text2 += ch;
8456 }
8457 #else
8458 wxString text2 = text;
8459 #endif
8460 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
8461
8462 if (GetRichTextCtrl())
8463 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8464
8465 success = true;
8466 }
8467 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8468 {
8469 wxBitmapDataObject data;
8470 wxTheClipboard->GetData(data);
8471 wxBitmap bitmap(data.GetBitmap());
8472 wxImage image(bitmap.ConvertToImage());
8473
8474 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
8475
8476 action->GetNewParagraphs().AddImage(image);
8477
8478 if (action->GetNewParagraphs().GetChildCount() == 1)
8479 action->GetNewParagraphs().SetPartialParagraph(true);
8480
8481 action->SetPosition(position+1);
8482
8483 // Set the range we'll need to delete in Undo
8484 action->SetRange(wxRichTextRange(position+1, position+1));
8485
8486 SubmitAction(action);
8487
8488 success = true;
8489 }
8490 wxTheClipboard->Close();
8491 }
8492 }
8493 #else
8494 wxUnusedVar(position);
8495 #endif
8496 return success;
8497 }
8498
8499 /// Can we paste from the clipboard?
8500 bool wxRichTextBuffer::CanPasteFromClipboard() const
8501 {
8502 bool canPaste = false;
8503 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8504 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8505 {
8506 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8507 #if wxUSE_UNICODE
8508 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8509 #endif
8510 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8511 wxTheClipboard->IsSupported(wxDF_BITMAP))
8512 {
8513 canPaste = true;
8514 }
8515 wxTheClipboard->Close();
8516 }
8517 #endif
8518 return canPaste;
8519 }
8520
8521 /// Dumps contents of buffer for debugging purposes
8522 void wxRichTextBuffer::Dump()
8523 {
8524 wxString text;
8525 {
8526 wxStringOutputStream stream(& text);
8527 wxTextOutputStream textStream(stream);
8528 Dump(textStream);
8529 }
8530
8531 wxLogDebug(text);
8532 }
8533
8534 /// Add an event handler
8535 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8536 {
8537 m_eventHandlers.Append(handler);
8538 return true;
8539 }
8540
8541 /// Remove an event handler
8542 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8543 {
8544 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8545 if (node)
8546 {
8547 m_eventHandlers.Erase(node);
8548 if (deleteHandler)
8549 delete handler;
8550
8551 return true;
8552 }
8553 else
8554 return false;
8555 }
8556
8557 /// Clear event handlers
8558 void wxRichTextBuffer::ClearEventHandlers()
8559 {
8560 m_eventHandlers.Clear();
8561 }
8562
8563 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8564 /// otherwise will stop at the first successful one.
8565 bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8566 {
8567 bool success = false;
8568 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8569 {
8570 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8571 if (handler->ProcessEvent(event))
8572 {
8573 success = true;
8574 if (!sendToAll)
8575 return true;
8576 }
8577 }
8578 return success;
8579 }
8580
8581 /// Set style sheet and notify of the change
8582 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8583 {
8584 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
8585
8586 wxWindowID winid = wxID_ANY;
8587 if (GetRichTextCtrl())
8588 winid = GetRichTextCtrl()->GetId();
8589
8590 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
8591 event.SetEventObject(GetRichTextCtrl());
8592 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
8593 event.SetOldStyleSheet(oldSheet);
8594 event.SetNewStyleSheet(sheet);
8595 event.Allow();
8596
8597 if (SendEvent(event) && !event.IsAllowed())
8598 {
8599 if (sheet != oldSheet)
8600 delete sheet;
8601
8602 return false;
8603 }
8604
8605 if (oldSheet && oldSheet != sheet)
8606 delete oldSheet;
8607
8608 SetStyleSheet(sheet);
8609
8610 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8611 event.SetOldStyleSheet(NULL);
8612 event.Allow();
8613
8614 return SendEvent(event);
8615 }
8616
8617 /// Set renderer, deleting old one
8618 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8619 {
8620 if (sm_renderer)
8621 delete sm_renderer;
8622 sm_renderer = renderer;
8623 }
8624
8625 /// Hit-testing: returns a flag indicating hit test details, plus
8626 /// information about position
8627 int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
8628 {
8629 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
8630 if (ret != wxRICHTEXT_HITTEST_NONE)
8631 {
8632 return ret;
8633 }
8634 else
8635 {
8636 textPosition = m_ownRange.GetEnd()-1;
8637 *obj = this;
8638 *contextObj = this;
8639 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8640 }
8641 }
8642
8643 void wxRichTextBuffer::SetFontScale(double fontScale)
8644 {
8645 m_fontScale = fontScale;
8646 m_fontTable.SetFontScale(fontScale);
8647 }
8648
8649 void wxRichTextBuffer::SetDimensionScale(double dimScale)
8650 {
8651 m_dimensionScale = dimScale;
8652 }
8653
8654 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
8655 {
8656 if (bulletAttr.GetTextColour().IsOk())
8657 {
8658 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8659 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
8660 }
8661 else
8662 {
8663 wxCheckSetPen(dc, *wxBLACK_PEN);
8664 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8665 }
8666
8667 wxFont font;
8668 if (bulletAttr.HasFont())
8669 {
8670 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8671 }
8672 else
8673 font = (*wxNORMAL_FONT);
8674
8675 wxCheckSetFont(dc, font);
8676
8677 int charHeight = dc.GetCharHeight();
8678
8679 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8680 int bulletHeight = bulletWidth;
8681
8682 int x = rect.x;
8683
8684 // Calculate the top position of the character (as opposed to the whole line height)
8685 int y = rect.y + (rect.height - charHeight);
8686
8687 // Calculate where the bullet should be positioned
8688 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
8689
8690 // The margin between a bullet and text.
8691 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8692
8693 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8694 x = rect.x + rect.width - bulletWidth - margin;
8695 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8696 x = x + (rect.width)/2 - bulletWidth/2;
8697
8698 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8699 {
8700 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8701 }
8702 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8703 {
8704 wxPoint pts[5];
8705 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8706 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8707 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8708 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
8709
8710 dc.DrawPolygon(4, pts);
8711 }
8712 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8713 {
8714 wxPoint pts[3];
8715 pts[0].x = x; pts[0].y = y;
8716 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8717 pts[2].x = x; pts[2].y = y + bulletHeight;
8718
8719 dc.DrawPolygon(3, pts);
8720 }
8721 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8722 {
8723 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8724 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8725 }
8726 else // "standard/circle", and catch-all
8727 {
8728 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8729 }
8730
8731 return true;
8732 }
8733
8734 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
8735 {
8736 if (!text.empty())
8737 {
8738 wxFont font;
8739 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8740 {
8741 wxRichTextAttr fontAttr;
8742 if (attr.HasFontPixelSize())
8743 fontAttr.SetFontPixelSize(attr.GetFontSize());
8744 else
8745 fontAttr.SetFontPointSize(attr.GetFontSize());
8746 fontAttr.SetFontStyle(attr.GetFontStyle());
8747 fontAttr.SetFontWeight(attr.GetFontWeight());
8748 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8749 fontAttr.SetFontFaceName(attr.GetBulletFont());
8750 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8751 }
8752 else if (attr.HasFont())
8753 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
8754 else
8755 font = (*wxNORMAL_FONT);
8756
8757 wxCheckSetFont(dc, font);
8758
8759 if (attr.GetTextColour().IsOk())
8760 dc.SetTextForeground(attr.GetTextColour());
8761
8762 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
8763
8764 int charHeight = dc.GetCharHeight();
8765 wxCoord tw, th;
8766 dc.GetTextExtent(text, & tw, & th);
8767
8768 int x = rect.x;
8769
8770 // Calculate the top position of the character (as opposed to the whole line height)
8771 int y = rect.y + (rect.height - charHeight);
8772
8773 // The margin between a bullet and text.
8774 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8775
8776 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8777 x = (rect.x + rect.width) - tw - margin;
8778 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8779 x = x + (rect.width)/2 - tw/2;
8780
8781 dc.DrawText(text, x, y);
8782
8783 return true;
8784 }
8785 else
8786 return false;
8787 }
8788
8789 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
8790 {
8791 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8792 // with the buffer. The store will allow retrieval from memory, disk or other means.
8793 return false;
8794 }
8795
8796 /// Enumerate the standard bullet names currently supported
8797 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8798 {
8799 bulletNames.Add(wxTRANSLATE("standard/circle"));
8800 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
8801 bulletNames.Add(wxTRANSLATE("standard/square"));
8802 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8803 bulletNames.Add(wxTRANSLATE("standard/triangle"));
8804
8805 return true;
8806 }
8807
8808 /*!
8809 * wxRichTextBox
8810 */
8811
8812 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
8813
8814 wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
8815 wxRichTextParagraphLayoutBox(parent)
8816 {
8817 }
8818
8819 /// Draw the item
8820 bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8821 {
8822 if (!IsShown())
8823 return true;
8824
8825 // TODO: if the active object in the control, draw an indication.
8826 // We need to add the concept of active object, and not just focus object,
8827 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8828 // Ultimately we would like to be able to interactively resize an active object
8829 // using drag handles.
8830 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8831 }
8832
8833 /// Copy
8834 void wxRichTextBox::Copy(const wxRichTextBox& obj)
8835 {
8836 wxRichTextParagraphLayoutBox::Copy(obj);
8837 }
8838
8839 // Edit properties via a GUI
8840 bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8841 {
8842 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8843 boxDlg.SetAttributes(GetAttributes());
8844
8845 if (boxDlg.ShowModal() == wxID_OK)
8846 {
8847 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8848 // indeterminate in the object.
8849 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8850 return true;
8851 }
8852 else
8853 return false;
8854 }
8855
8856 /*!
8857 * wxRichTextField
8858 */
8859
8860 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8861
8862 wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8863 wxRichTextParagraphLayoutBox(parent)
8864 {
8865 SetFieldType(fieldType);
8866 }
8867
8868 /// Draw the item
8869 bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8870 {
8871 if (!IsShown())
8872 return true;
8873
8874 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8875 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8876 return true;
8877
8878 // Fallback; but don't draw guidelines.
8879 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8880 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8881 }
8882
8883 bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8884 {
8885 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8886 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8887 return true;
8888
8889 // Fallback
8890 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8891 }
8892
8893 bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
8894 {
8895 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8896 if (fieldType)
8897 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
8898
8899 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
8900 }
8901
8902 /// Calculate range
8903 void wxRichTextField::CalculateRange(long start, long& end)
8904 {
8905 if (IsTopLevel())
8906 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8907 else
8908 wxRichTextObject::CalculateRange(start, end);
8909 }
8910
8911 /// Copy
8912 void wxRichTextField::Copy(const wxRichTextField& obj)
8913 {
8914 wxRichTextParagraphLayoutBox::Copy(obj);
8915
8916 UpdateField(GetBuffer());
8917 }
8918
8919 // Edit properties via a GUI
8920 bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8921 {
8922 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8923 if (fieldType)
8924 return fieldType->EditProperties(this, parent, buffer);
8925
8926 return false;
8927 }
8928
8929 bool wxRichTextField::CanEditProperties() const
8930 {
8931 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8932 if (fieldType)
8933 return fieldType->CanEditProperties((wxRichTextField*) this);
8934
8935 return false;
8936 }
8937
8938 wxString wxRichTextField::GetPropertiesMenuLabel() const
8939 {
8940 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8941 if (fieldType)
8942 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8943
8944 return wxEmptyString;
8945 }
8946
8947 bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
8948 {
8949 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8950 if (fieldType)
8951 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
8952
8953 return false;
8954 }
8955
8956 bool wxRichTextField::IsTopLevel() const
8957 {
8958 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8959 if (fieldType)
8960 return fieldType->IsTopLevel((wxRichTextField*) this);
8961
8962 return true;
8963 }
8964
8965 IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8966
8967 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8968
8969 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8970 {
8971 Init();
8972
8973 SetName(name);
8974 SetLabel(label);
8975 SetDisplayStyle(displayStyle);
8976 }
8977
8978 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8979 {
8980 Init();
8981
8982 SetName(name);
8983 SetBitmap(bitmap);
8984 SetDisplayStyle(displayStyle);
8985 }
8986
8987 void wxRichTextFieldTypeStandard::Init()
8988 {
8989 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8990 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8991 m_textColour = *wxWHITE;
8992 m_borderColour = *wxBLACK;
8993 m_backgroundColour = *wxBLACK;
8994 m_verticalPadding = 1;
8995 m_horizontalPadding = 3;
8996 m_horizontalMargin = 2;
8997 m_verticalMargin = 0;
8998 }
8999
9000 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
9001 {
9002 wxRichTextFieldType::Copy(field);
9003
9004 m_label = field.m_label;
9005 m_displayStyle = field.m_displayStyle;
9006 m_font = field.m_font;
9007 m_textColour = field.m_textColour;
9008 m_borderColour = field.m_borderColour;
9009 m_backgroundColour = field.m_backgroundColour;
9010 m_verticalPadding = field.m_verticalPadding;
9011 m_horizontalPadding = field.m_horizontalPadding;
9012 m_horizontalMargin = field.m_horizontalMargin;
9013 m_bitmap = field.m_bitmap;
9014 }
9015
9016 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))
9017 {
9018 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9019 return false; // USe default composite drawing
9020 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9021 {
9022 int borderSize = 1;
9023
9024 wxPen borderPen(m_borderColour, 1, wxSOLID);
9025 wxBrush backgroundBrush(m_backgroundColour);
9026 wxColour textColour(m_textColour);
9027
9028 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9029 {
9030 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
9031 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
9032
9033 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
9034 backgroundBrush = wxBrush(highlightColour);
9035
9036 wxCheckSetBrush(dc, backgroundBrush);
9037 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9038 dc.DrawRectangle(rect);
9039 }
9040
9041 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9042 borderSize = 0;
9043
9044 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9045 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9046 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9047
9048 // clientArea is where the text is actually written
9049 wxRect clientArea = objectRect;
9050
9051 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9052 {
9053 dc.SetPen(borderPen);
9054 dc.SetBrush(backgroundBrush);
9055 dc.DrawRoundedRectangle(objectRect, 4.0);
9056 }
9057 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9058 {
9059 int arrowLength = objectRect.height/2;
9060 clientArea.width -= (arrowLength - m_horizontalPadding);
9061
9062 wxPoint pts[5];
9063 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9064 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9065 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9066 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9067 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9068 dc.SetPen(borderPen);
9069 dc.SetBrush(backgroundBrush);
9070 dc.DrawPolygon(5, pts);
9071 }
9072 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9073 {
9074 int arrowLength = objectRect.height/2;
9075 clientArea.width -= (arrowLength - m_horizontalPadding);
9076 clientArea.x += (arrowLength - m_horizontalPadding);
9077
9078 wxPoint pts[5];
9079 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9080 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9081 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9082 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9083 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9084 dc.SetPen(borderPen);
9085 dc.SetBrush(backgroundBrush);
9086 dc.DrawPolygon(5, pts);
9087 }
9088
9089 if (m_bitmap.IsOk())
9090 {
9091 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9092 int y = clientArea.y + m_verticalPadding;
9093 dc.DrawBitmap(m_bitmap, x, y, true);
9094
9095 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9096 {
9097 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9098 wxCheckSetPen(dc, *wxBLACK_PEN);
9099 dc.SetLogicalFunction(wxINVERT);
9100 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9101 dc.SetLogicalFunction(wxCOPY);
9102 }
9103 }
9104 else
9105 {
9106 wxString label(m_label);
9107 if (label.IsEmpty())
9108 label = wxT("??");
9109 int w, h, maxDescent;
9110 dc.SetFont(m_font);
9111 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9112 dc.SetTextForeground(textColour);
9113
9114 int x = clientArea.x + (clientArea.width - w)/2;
9115 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9116 dc.DrawText(m_label, x, y);
9117 }
9118 }
9119
9120 return true;
9121 }
9122
9123 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9124 {
9125 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9126 return false; // USe default composite layout
9127
9128 wxSize size = GetSize(obj, dc, context, style);
9129 obj->SetCachedSize(size);
9130 obj->SetMinSize(size);
9131 obj->SetMaxSize(size);
9132 return true;
9133 }
9134
9135 bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
9136 {
9137 if (IsTopLevel(obj))
9138 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
9139 else
9140 {
9141 wxSize sz = GetSize(obj, dc, context, 0);
9142 if (partialExtents)
9143 {
9144 int lastSize;
9145 if (partialExtents->GetCount() > 0)
9146 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9147 else
9148 lastSize = 0;
9149 partialExtents->Add(lastSize + sz.x);
9150 }
9151 size = sz;
9152 return true;
9153 }
9154 }
9155
9156 wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9157 {
9158 int borderSize = 1;
9159 int w = 0, h = 0, maxDescent = 0;
9160
9161 wxSize sz;
9162 if (m_bitmap.IsOk())
9163 {
9164 w = m_bitmap.GetWidth();
9165 h = m_bitmap.GetHeight();
9166
9167 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9168 }
9169 else
9170 {
9171 wxString label(m_label);
9172 if (label.IsEmpty())
9173 label = wxT("??");
9174 dc.SetFont(m_font);
9175 dc.GetTextExtent(label, & w, &h, & maxDescent);
9176
9177 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9178 }
9179
9180 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9181 {
9182 sz.x += borderSize*2;
9183 sz.y += borderSize*2;
9184 }
9185
9186 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9187 {
9188 // Add space for the arrow
9189 sz.x += (sz.y/2 - m_horizontalPadding);
9190 }
9191
9192 return sz;
9193 }
9194
9195 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9196
9197 wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9198 wxRichTextBox(parent)
9199 {
9200 }
9201
9202 /// Draw the item
9203 bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9204 {
9205 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9206 }
9207
9208 /// Copy
9209 void wxRichTextCell::Copy(const wxRichTextCell& obj)
9210 {
9211 wxRichTextBox::Copy(obj);
9212 }
9213
9214 // Edit properties via a GUI
9215 bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9216 {
9217 // We need to gather common attributes for all selected cells.
9218
9219 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9220 bool multipleCells = false;
9221 wxRichTextAttr attr;
9222
9223 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9224 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9225 {
9226 wxRichTextAttr clashingAttr, absentAttr;
9227 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9228 size_t i;
9229 int selectedCellCount = 0;
9230 for (i = 0; i < sel.GetCount(); i++)
9231 {
9232 const wxRichTextRange& range = sel[i];
9233 wxRichTextCell* cell = table->GetCell(range.GetStart());
9234 if (cell)
9235 {
9236 wxRichTextAttr cellStyle = cell->GetAttributes();
9237
9238 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9239
9240 selectedCellCount ++;
9241 }
9242 }
9243 multipleCells = selectedCellCount > 1;
9244 }
9245 else
9246 {
9247 attr = GetAttributes();
9248 }
9249
9250 wxString caption;
9251 if (multipleCells)
9252 caption = _("Multiple Cell Properties");
9253 else
9254 caption = _("Cell Properties");
9255
9256 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9257 cellDlg.SetAttributes(attr);
9258
9259 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
9260 if (sizePage)
9261 {
9262 // We don't want position and floating controls for a cell.
9263 sizePage->ShowPositionControls(false);
9264 sizePage->ShowFloatingControls(false);
9265 }
9266
9267 if (cellDlg.ShowModal() == wxID_OK)
9268 {
9269 if (multipleCells)
9270 {
9271 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9272 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9273 // since it may represent clashing attributes across multiple objects.
9274 table->SetCellStyle(sel, attr);
9275 }
9276 else
9277 // For a single object, indeterminate attributes set by the user should be reflected in the
9278 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9279 // the style directly instead of applying (which ignores indeterminate attributes,
9280 // leaving them as they were).
9281 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9282 return true;
9283 }
9284 else
9285 return false;
9286 }
9287
9288 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9289
9290 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9291
9292 wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9293 {
9294 m_rowCount = 0;
9295 m_colCount = 0;
9296 }
9297
9298 // Draws the object.
9299 bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9300 {
9301 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9302 }
9303
9304 WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9305 WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9306
9307 // Lays the object out. rect is the space available for layout. Often it will
9308 // be the specified overall space for this object, if trying to constrain
9309 // layout to a particular size, or it could be the total space available in the
9310 // parent. rect is the overall size, so we must subtract margins and padding.
9311 // to get the actual available space.
9312 bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
9313 {
9314 SetPosition(rect.GetPosition());
9315
9316 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9317 // minimum size if within alloted size, then divide up remaining size
9318 // between rows/cols.
9319
9320 double scale = 1.0;
9321 wxRichTextBuffer* buffer = GetBuffer();
9322 if (buffer) scale = buffer->GetScale();
9323
9324 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
9325 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9326
9327 wxRichTextAttr attr(GetAttributes());
9328 context.ApplyVirtualAttributes(attr, this);
9329
9330 // If we have no fixed table size, and assuming we're not pushed for
9331 // space, then we don't have to try to stretch the table to fit the contents.
9332 bool stretchToFitTableWidth = false;
9333
9334 int tableWidth = rect.width;
9335 if (attr.GetTextBoxAttr().GetWidth().IsValid())
9336 {
9337 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
9338
9339 // Fixed table width, so we do want to stretch columns out if necessary.
9340 stretchToFitTableWidth = true;
9341
9342 // Shouldn't be able to exceed the size passed to this function
9343 tableWidth = wxMin(rect.width, tableWidth);
9344 }
9345
9346 // Get internal padding
9347 int paddingLeft = 0, paddingTop = 0;
9348 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9349 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9350 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9351 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
9352
9353 // Assume that left and top padding are also used for inter-cell padding.
9354 int paddingX = paddingLeft;
9355 int paddingY = paddingTop;
9356
9357 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
9358 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
9359
9360 // Internal table width - the area for content
9361 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9362
9363 int rowCount = m_cells.GetCount();
9364 if (m_colCount == 0 || rowCount == 0)
9365 {
9366 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9367 SetCachedSize(overallRect.GetSize());
9368
9369 // Zero content size
9370 SetMinSize(overallRect.GetSize());
9371 SetMaxSize(GetMinSize());
9372 return true;
9373 }
9374
9375 // The final calculated widths
9376 wxArrayInt colWidths;
9377 colWidths.Add(0, m_colCount);
9378
9379 wxArrayInt absoluteColWidths;
9380 absoluteColWidths.Add(0, m_colCount);
9381
9382 wxArrayInt percentageColWidths;
9383 percentageColWidths.Add(0, m_colCount);
9384 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9385 // These are only relevant when the first column contains spanning information.
9386 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9387 wxArrayInt maxColWidths;
9388 maxColWidths.Add(0, m_colCount);
9389 wxArrayInt minColWidths;
9390 minColWidths.Add(0, m_colCount);
9391
9392 wxSize tableSize(tableWidth, 0);
9393
9394 int i, j, k;
9395
9396 for (i = 0; i < m_colCount; i++)
9397 {
9398 absoluteColWidths[i] = 0;
9399 // absoluteColWidthsSpanning[i] = 0;
9400 percentageColWidths[i] = -1;
9401 // percentageColWidthsSpanning[i] = -1;
9402 colWidths[i] = 0;
9403 maxColWidths[i] = 0;
9404 minColWidths[i] = 0;
9405 // columnSpans[i] = 1;
9406 }
9407
9408 // (0) Determine which cells are visible according to spans
9409 // 1 2 3 4 5
9410 // __________________
9411 // | | | | | 1
9412 // |------| |----|
9413 // |------| | | 2
9414 // |------| | | 3
9415 // |------------------|
9416 // |__________________| 4
9417
9418 // To calculate cell visibility:
9419 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9420 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9421 // that cell, hide the cell.
9422
9423 // We can also use this array to match the size of spanning cells to the grid. Or just do
9424 // this when we iterate through all cells.
9425
9426 // 0.1: add spanning cells to an array
9427 wxRichTextRectArray rectArray;
9428 for (j = 0; j < m_rowCount; j++)
9429 {
9430 for (i = 0; i < m_colCount; i++)
9431 {
9432 wxRichTextBox* cell = GetCell(j, i);
9433 int colSpan = 1, rowSpan = 1;
9434 if (cell->GetProperties().HasProperty(wxT("colspan")))
9435 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9436 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9437 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9438 if (colSpan > 1 || rowSpan > 1)
9439 {
9440 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9441 }
9442 }
9443 }
9444 // 0.2: find which cells are subsumed by a spanning cell
9445 for (j = 0; j < m_rowCount; j++)
9446 {
9447 for (i = 0; i < m_colCount; i++)
9448 {
9449 wxRichTextBox* cell = GetCell(j, i);
9450 if (rectArray.GetCount() == 0)
9451 {
9452 cell->Show(true);
9453 }
9454 else
9455 {
9456 int colSpan = 1, rowSpan = 1;
9457 if (cell->GetProperties().HasProperty(wxT("colspan")))
9458 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9459 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9460 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9461 if (colSpan > 1 || rowSpan > 1)
9462 {
9463 // Assume all spanning cells are shown
9464 cell->Show(true);
9465 }
9466 else
9467 {
9468 bool shown = true;
9469 for (k = 0; k < (int) rectArray.GetCount(); k++)
9470 {
9471 if (rectArray[k].Contains(wxPoint(i, j)))
9472 {
9473 shown = false;
9474 break;
9475 }
9476 }
9477 cell->Show(shown);
9478 }
9479 }
9480 }
9481 }
9482
9483 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9484 // overlap with a spanned cell starting at a previous column position.
9485 // This means we need to keep an array of rects so we can check. However
9486 // it does also mean that some spans simply may not be taken into account
9487 // where there are different spans happening on different rows. In these cases,
9488 // they will simply be as wide as their constituent columns.
9489
9490 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9491 // the absolute or percentage width of each column.
9492
9493 for (j = 0; j < m_rowCount; j++)
9494 {
9495 // First get the overall margins so we can calculate percentage widths based on
9496 // the available content space for all cells on the row
9497
9498 int overallRowContentMargin = 0;
9499 int visibleCellCount = 0;
9500
9501 for (i = 0; i < m_colCount; i++)
9502 {
9503 wxRichTextBox* cell = GetCell(j, i);
9504 if (cell->IsShown())
9505 {
9506 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9507 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9508
9509 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9510 visibleCellCount ++;
9511 }
9512 }
9513
9514 // Add in inter-cell padding
9515 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9516
9517 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9518 wxSize rowTableSize(rowContentWidth, 0);
9519 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9520
9521 for (i = 0; i < m_colCount; i++)
9522 {
9523 wxRichTextBox* cell = GetCell(j, i);
9524 if (cell->IsShown())
9525 {
9526 int colSpan = 1;
9527 if (cell->GetProperties().HasProperty(wxT("colspan")))
9528 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9529
9530 // Lay out cell to find min/max widths
9531 cell->Invalidate(wxRICHTEXT_ALL);
9532 cell->Layout(dc, context, availableSpace, availableSpace, style);
9533
9534 if (colSpan == 1)
9535 {
9536 int absoluteCellWidth = -1;
9537 int percentageCellWidth = -1;
9538
9539 // I think we need to calculate percentages from the internal table size,
9540 // minus the padding between cells which we'll need to calculate from the
9541 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9542 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9543 // so if we want to conform to that we'll need to add in the overall cell margins.
9544 // However, this will make it difficult to specify percentages that add up to
9545 // 100% and still fit within the table width.
9546 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9547 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9548 // If we're using internal content size for the width, we would calculate the
9549 // the overall cell width for n cells as:
9550 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9551 // + thisOverallCellMargin
9552 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9553 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9554
9555 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9556 {
9557 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9558 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9559 {
9560 percentageCellWidth = w;
9561 }
9562 else
9563 {
9564 absoluteCellWidth = w;
9565 }
9566 // Override absolute width with minimum width if necessary
9567 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9568 absoluteCellWidth = cell->GetMinSize().x;
9569 }
9570
9571 if (absoluteCellWidth != -1)
9572 {
9573 if (absoluteCellWidth > absoluteColWidths[i])
9574 absoluteColWidths[i] = absoluteCellWidth;
9575 }
9576
9577 if (percentageCellWidth != -1)
9578 {
9579 if (percentageCellWidth > percentageColWidths[i])
9580 percentageColWidths[i] = percentageCellWidth;
9581 }
9582
9583 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9584 minColWidths[i] = cell->GetMinSize().x;
9585 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9586 maxColWidths[i] = cell->GetMaxSize().x;
9587 }
9588 }
9589 }
9590 }
9591
9592 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9593 // TODO: simply merge this into (1).
9594 for (i = 0; i < m_colCount; i++)
9595 {
9596 if (absoluteColWidths[i] > 0)
9597 {
9598 colWidths[i] = absoluteColWidths[i];
9599 }
9600 else if (percentageColWidths[i] > 0)
9601 {
9602 colWidths[i] = percentageColWidths[i];
9603
9604 // This is rubbish - we calculated the absolute widths from percentages, so
9605 // we can't do it again here.
9606 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9607 }
9608 }
9609
9610 // (3) Process absolute or proportional widths of spanning columns,
9611 // now that we know what our fixed column widths are going to be.
9612 // Spanned cells will try to adjust columns so the span will fit.
9613 // Even existing fixed column widths can be expanded if necessary.
9614 // Actually, currently fixed columns widths aren't adjusted; instead,
9615 // the algorithm favours earlier rows and adjusts unspecified column widths
9616 // the first time only. After that, we can't know whether the column has been
9617 // specified explicitly or not. (We could make a note if necessary.)
9618 for (j = 0; j < m_rowCount; j++)
9619 {
9620 // First get the overall margins so we can calculate percentage widths based on
9621 // the available content space for all cells on the row
9622
9623 int overallRowContentMargin = 0;
9624 int visibleCellCount = 0;
9625
9626 for (i = 0; i < m_colCount; i++)
9627 {
9628 wxRichTextBox* cell = GetCell(j, i);
9629 if (cell->IsShown())
9630 {
9631 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9632 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9633
9634 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9635 visibleCellCount ++;
9636 }
9637 }
9638
9639 // Add in inter-cell padding
9640 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9641
9642 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9643 wxSize rowTableSize(rowContentWidth, 0);
9644 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9645
9646 for (i = 0; i < m_colCount; i++)
9647 {
9648 wxRichTextBox* cell = GetCell(j, i);
9649 if (cell->IsShown())
9650 {
9651 int colSpan = 1;
9652 if (cell->GetProperties().HasProperty(wxT("colspan")))
9653 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9654
9655 if (colSpan > 1)
9656 {
9657 int spans = wxMin(colSpan, m_colCount - i);
9658 int cellWidth = 0;
9659 if (spans > 0)
9660 {
9661 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9662 {
9663 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9664 // Override absolute width with minimum width if necessary
9665 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9666 cellWidth = cell->GetMinSize().x;
9667 }
9668 else
9669 {
9670 // Do we want to do this? It's the only chance we get to
9671 // use the cell's min/max sizes, so we need to work out
9672 // how we're going to balance the unspecified spanning cell
9673 // width with the possibility more-constrained constituent cell widths.
9674 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9675 // don't want to constraint all the spanned columns to fit into this cell.
9676 // OK, let's say that if any of the constituent columns don't fit,
9677 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9678 // cells to the columns later.
9679 cellWidth = cell->GetMinSize().x;
9680 if (cell->GetMaxSize().x > cellWidth)
9681 cellWidth = cell->GetMaxSize().x;
9682 }
9683
9684 // Subtract the padding between cells
9685 int spanningWidth = cellWidth;
9686 spanningWidth -= paddingX * (spans-1);
9687
9688 if (spanningWidth > 0)
9689 {
9690 // Now share the spanning width between columns within that span
9691 // TODO: take into account min widths of columns within the span
9692 int spanningWidthLeft = spanningWidth;
9693 int stretchColCount = 0;
9694 for (k = i; k < (i+spans); k++)
9695 {
9696 if (colWidths[k] > 0) // absolute or proportional width has been specified
9697 spanningWidthLeft -= colWidths[k];
9698 else
9699 stretchColCount ++;
9700 }
9701 // Now divide what's left between the remaining columns
9702 int colShare = 0;
9703 if (stretchColCount > 0)
9704 colShare = spanningWidthLeft / stretchColCount;
9705 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9706
9707 // If fixed-width columns are currently too big, then we'll later
9708 // stretch the spanned cell to fit.
9709
9710 if (spanningWidthLeft > 0)
9711 {
9712 for (k = i; k < (i+spans); k++)
9713 {
9714 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9715 {
9716 int newWidth = colShare;
9717 if (k == (i+spans-1))
9718 newWidth += colShareRemainder; // ensure all pixels are filled
9719 colWidths[k] = newWidth;
9720 }
9721 }
9722 }
9723 }
9724 }
9725 }
9726 }
9727 }
9728 }
9729
9730 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9731 // TODO: take into account min widths of columns within the span
9732 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9733 int widthLeft = tableWidthMinusPadding;
9734 int stretchColCount = 0;
9735 for (i = 0; i < m_colCount; i++)
9736 {
9737 // TODO: we need to take into account min widths.
9738 // Subtract min width from width left, then
9739 // add the colShare to the min width
9740 if (colWidths[i] > 0) // absolute or proportional width has been specified
9741 widthLeft -= colWidths[i];
9742 else
9743 {
9744 if (minColWidths[i] > 0)
9745 widthLeft -= minColWidths[i];
9746
9747 stretchColCount ++;
9748 }
9749 }
9750
9751 // Now divide what's left between the remaining columns
9752 int colShare = 0;
9753 if (stretchColCount > 0)
9754 colShare = widthLeft / stretchColCount;
9755 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9756
9757 // Check we don't have enough space, in which case shrink all columns, overriding
9758 // any absolute/proportional widths
9759 // TODO: actually we would like to divide up the shrinkage according to size.
9760 // How do we calculate the proportions that will achieve this?
9761 // Could first choose an arbitrary value for stretching cells, and then calculate
9762 // factors to multiply each width by.
9763 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9764 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9765 {
9766 colShare = tableWidthMinusPadding / m_colCount;
9767 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9768 for (i = 0; i < m_colCount; i++)
9769 {
9770 colWidths[i] = 0;
9771 minColWidths[i] = 0;
9772 }
9773 }
9774
9775 // We have to adjust the columns if either we need to shrink the
9776 // table to fit the parent/table width, or we explicitly set the
9777 // table width and need to stretch out the table.
9778 if (widthLeft < 0 || stretchToFitTableWidth)
9779 {
9780 for (i = 0; i < m_colCount; i++)
9781 {
9782 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9783 {
9784 if (minColWidths[i] > 0)
9785 colWidths[i] = minColWidths[i] + colShare;
9786 else
9787 colWidths[i] = colShare;
9788 if (i == (m_colCount-1))
9789 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9790 }
9791 }
9792 }
9793
9794 // TODO: if spanned cells have no specified or max width, make them the
9795 // as big as the columns they span. Do this for all spanned cells in all
9796 // rows, of course. Size any spanned cells left over at the end - even if they
9797 // have width > 0, make sure they're limited to the appropriate column edge.
9798
9799
9800 /*
9801 Sort out confusion between content width
9802 and overall width later. For now, assume we specify overall width.
9803
9804 So, now we've laid out the table to fit into the given space
9805 and have used specified widths and minimum widths.
9806
9807 Now we need to consider how we will try to take maximum width into account.
9808
9809 */
9810
9811 // (??) TODO: take max width into account
9812
9813 // (6) Lay out all cells again with the current values
9814
9815 int maxRight = 0;
9816 int y = availableSpace.y;
9817 for (j = 0; j < m_rowCount; j++)
9818 {
9819 int x = availableSpace.x; // TODO: take into account centering etc.
9820 int maxCellHeight = 0;
9821 int maxSpecifiedCellHeight = 0;
9822
9823 wxArrayInt actualWidths;
9824 actualWidths.Add(0, m_colCount);
9825
9826 wxTextAttrDimensionConverter converter(dc, scale);
9827 for (i = 0; i < m_colCount; i++)
9828 {
9829 wxRichTextCell* cell = GetCell(j, i);
9830 if (cell->IsShown())
9831 {
9832 // Get max specified cell height
9833 // Don't handle percentages for height
9834 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9835 {
9836 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9837 if (h > maxSpecifiedCellHeight)
9838 maxSpecifiedCellHeight = h;
9839 }
9840
9841 if (colWidths[i] > 0) // absolute or proportional width has been specified
9842 {
9843 int colSpan = 1;
9844 if (cell->GetProperties().HasProperty(wxT("colspan")))
9845 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9846
9847 wxRect availableCellSpace;
9848
9849 // TODO: take into acount spans
9850 if (colSpan > 1)
9851 {
9852 // Calculate the size of this spanning cell from its constituent columns
9853 int xx = x;
9854 int spans = wxMin(colSpan, m_colCount - i);
9855 for (k = i; k < spans; k++)
9856 {
9857 if (k != i)
9858 xx += paddingX;
9859 xx += colWidths[k];
9860 }
9861 availableCellSpace = wxRect(x, y, xx, -1);
9862 }
9863 else
9864 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9865
9866 // Store actual width so we can force cell to be the appropriate width on the final loop
9867 actualWidths[i] = availableCellSpace.GetWidth();
9868
9869 // Lay out cell
9870 cell->Invalidate(wxRICHTEXT_ALL);
9871 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9872
9873 // TODO: use GetCachedSize().x to compute 'natural' size
9874
9875 x += (availableCellSpace.GetWidth() + paddingX);
9876 if (cell->GetCachedSize().y > maxCellHeight)
9877 maxCellHeight = cell->GetCachedSize().y;
9878 }
9879 }
9880 }
9881
9882 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9883
9884 for (i = 0; i < m_colCount; i++)
9885 {
9886 wxRichTextCell* cell = GetCell(j, i);
9887 if (cell->IsShown())
9888 {
9889 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9890 // Lay out cell with new height
9891 cell->Invalidate(wxRICHTEXT_ALL);
9892 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9893
9894 // Make sure the cell size really is the appropriate size,
9895 // not the calculated box size
9896 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9897
9898 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9899 }
9900 }
9901
9902 y += maxCellHeight;
9903 if (j < (m_rowCount-1))
9904 y += paddingY;
9905 }
9906
9907 // We need to add back the margins etc.
9908 {
9909 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9910 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
9911 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
9912 SetCachedSize(marginRect.GetSize());
9913 }
9914
9915 // TODO: calculate max size
9916 {
9917 SetMaxSize(GetCachedSize());
9918 }
9919
9920 // TODO: calculate min size
9921 {
9922 SetMinSize(GetCachedSize());
9923 }
9924
9925 // TODO: currently we use either a fixed table width or the parent's size.
9926 // We also want to be able to calculate the table width from its content,
9927 // whether using fixed column widths or cell content min/max width.
9928 // Probably need a boolean flag to say whether we need to stretch cells
9929 // to fit the table width, or to simply use min/max cell widths. The
9930 // trouble with this is that if cell widths are not specified, they
9931 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9932 // Anyway, ignoring that problem, we probably need to factor layout into a function
9933 // that can can calculate the maximum unconstrained layout in case table size is
9934 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9935 // constrain Layout(), or the previously-calculated max size to constraint layout.
9936
9937 return true;
9938 }
9939
9940 // Finds the absolute position and row height for the given character position
9941 bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
9942 {
9943 wxRichTextCell* child = GetCell(index+1);
9944 if (child)
9945 {
9946 // Find the position at the start of the child cell, since the table doesn't
9947 // have any caret position of its own.
9948 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
9949 }
9950 else
9951 return false;
9952 }
9953
9954 // Get the cell at the given character position (in the range of the table).
9955 wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9956 {
9957 int row = 0, col = 0;
9958 if (GetCellRowColumnPosition(pos, row, col))
9959 {
9960 return GetCell(row, col);
9961 }
9962 else
9963 return NULL;
9964 }
9965
9966 // Get the row/column for a given character position
9967 bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9968 {
9969 if (m_colCount == 0 || m_rowCount == 0)
9970 return false;
9971
9972 row = (int) (pos / m_colCount);
9973 col = pos - (row * m_colCount);
9974
9975 wxASSERT(row < m_rowCount && col < m_colCount);
9976
9977 if (row < m_rowCount && col < m_colCount)
9978 return true;
9979 else
9980 return false;
9981 }
9982
9983 // Calculate range, taking row/cell ordering into account instead of relying
9984 // on list ordering.
9985 void wxRichTextTable::CalculateRange(long start, long& end)
9986 {
9987 long current = start;
9988 long lastEnd = current;
9989
9990 if (IsTopLevel())
9991 {
9992 current = 0;
9993 lastEnd = 0;
9994 }
9995
9996 int i, j;
9997 for (i = 0; i < m_rowCount; i++)
9998 {
9999 for (j = 0; j < m_colCount; j++)
10000 {
10001 wxRichTextCell* child = GetCell(i, j);
10002 if (child)
10003 {
10004 long childEnd = 0;
10005
10006 child->CalculateRange(current, childEnd);
10007
10008 lastEnd = childEnd;
10009 current = childEnd + 1;
10010 }
10011 }
10012 }
10013
10014 // A top-level object always has a range of size 1,
10015 // because its children don't count at this level.
10016 end = start;
10017 m_range.SetRange(start, start);
10018
10019 // An object with no children has zero length
10020 if (m_children.GetCount() == 0)
10021 lastEnd --;
10022 m_ownRange.SetRange(0, lastEnd);
10023 }
10024
10025 // Gets the range size.
10026 bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
10027 {
10028 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
10029 }
10030
10031 // Deletes content in the given range.
10032 bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
10033 {
10034 // TODO: implement deletion of cells
10035 return true;
10036 }
10037
10038 // Gets any text in this object for the given range.
10039 wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10040 {
10041 return wxRichTextBox::GetTextForRange(range);
10042 }
10043
10044 // Copies this object.
10045 void wxRichTextTable::Copy(const wxRichTextTable& obj)
10046 {
10047 wxRichTextBox::Copy(obj);
10048
10049 ClearTable();
10050
10051 m_rowCount = obj.m_rowCount;
10052 m_colCount = obj.m_colCount;
10053
10054 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10055
10056 int i, j;
10057 for (i = 0; i < m_rowCount; i++)
10058 {
10059 wxRichTextObjectPtrArray& colArray = m_cells[i];
10060 for (j = 0; j < m_colCount; j++)
10061 {
10062 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10063 AppendChild(cell);
10064
10065 colArray.Add(cell);
10066 }
10067 }
10068 }
10069
10070 void wxRichTextTable::ClearTable()
10071 {
10072 m_cells.Clear();
10073 DeleteChildren();
10074 }
10075
10076 bool wxRichTextTable::CreateTable(int rows, int cols)
10077 {
10078 ClearTable();
10079
10080 m_rowCount = rows;
10081 m_colCount = cols;
10082
10083 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10084
10085 int i, j;
10086 for (i = 0; i < rows; i++)
10087 {
10088 wxRichTextObjectPtrArray& colArray = m_cells[i];
10089 for (j = 0; j < cols; j++)
10090 {
10091 wxRichTextCell* cell = new wxRichTextCell;
10092 AppendChild(cell);
10093 cell->AddParagraph(wxEmptyString);
10094
10095 colArray.Add(cell);
10096 }
10097 }
10098
10099 return true;
10100 }
10101
10102 wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10103 {
10104 wxASSERT(row < m_rowCount);
10105 wxASSERT(col < m_colCount);
10106
10107 if (row < m_rowCount && col < m_colCount)
10108 {
10109 wxRichTextObjectPtrArray& colArray = m_cells[row];
10110 wxRichTextObject* obj = colArray[col];
10111 return wxDynamicCast(obj, wxRichTextCell);
10112 }
10113 else
10114 return NULL;
10115 }
10116
10117 // Returns a selection object specifying the selections between start and end character positions.
10118 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10119 wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10120 {
10121 wxRichTextSelection selection;
10122 selection.SetContainer((wxRichTextTable*) this);
10123
10124 if (start > end)
10125 {
10126 long tmp = end;
10127 end = start;
10128 start = tmp;
10129 }
10130
10131 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10132
10133 if (end >= (m_colCount * m_rowCount))
10134 return selection;
10135
10136 // We need to find the rectangle of cells that is described by the rectangle
10137 // with start, end as the diagonal. Make sure we don't add cells that are
10138 // not currenty visible because they are overlapped by spanning cells.
10139 /*
10140 --------------------------
10141 | 0 | 1 | 2 | 3 | 4 |
10142 --------------------------
10143 | 5 | 6 | 7 | 8 | 9 |
10144 --------------------------
10145 | 10 | 11 | 12 | 13 | 14 |
10146 --------------------------
10147 | 15 | 16 | 17 | 18 | 19 |
10148 --------------------------
10149
10150 Let's say we select 6 -> 18.
10151
10152 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10153 which is left and which is right.
10154
10155 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10156
10157 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10158 and (b) shown.
10159
10160
10161 */
10162
10163 int leftCol = start - m_colCount * int(start/m_colCount);
10164 int rightCol = end - m_colCount * int(end/m_colCount);
10165
10166 int topRow = int(start/m_colCount);
10167 int bottomRow = int(end/m_colCount);
10168
10169 if (leftCol > rightCol)
10170 {
10171 int tmp = rightCol;
10172 rightCol = leftCol;
10173 leftCol = tmp;
10174 }
10175
10176 if (topRow > bottomRow)
10177 {
10178 int tmp = bottomRow;
10179 bottomRow = topRow;
10180 topRow = tmp;
10181 }
10182
10183 int i, j;
10184 for (i = topRow; i <= bottomRow; i++)
10185 {
10186 for (j = leftCol; j <= rightCol; j++)
10187 {
10188 wxRichTextCell* cell = GetCell(i, j);
10189 if (cell && cell->IsShown())
10190 selection.Add(cell->GetRange());
10191 }
10192 }
10193
10194 return selection;
10195 }
10196
10197 // Sets the attributes for the cells specified by the selection.
10198 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10199 {
10200 if (selection.GetContainer() != this)
10201 return false;
10202
10203 wxRichTextBuffer* buffer = GetBuffer();
10204 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10205 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10206
10207 if (withUndo)
10208 buffer->BeginBatchUndo(_("Set Cell Style"));
10209
10210 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10211 while (node)
10212 {
10213 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10214 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10215 SetStyle(cell, style, flags);
10216 node = node->GetNext();
10217 }
10218
10219 // Do action, or delay it until end of batch.
10220 if (withUndo)
10221 buffer->EndBatchUndo();
10222
10223 return true;
10224 }
10225
10226 bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10227 {
10228 wxASSERT((startRow + noRows) < m_rowCount);
10229 if ((startRow + noRows) >= m_rowCount)
10230 return false;
10231
10232 int i, j;
10233 for (i = startRow; i < (startRow+noRows); i++)
10234 {
10235 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10236 for (j = 0; j < (int) colArray.GetCount(); j++)
10237 {
10238 wxRichTextObject* cell = colArray[j];
10239 RemoveChild(cell, true);
10240 }
10241
10242 // Keep deleting at the same position, since we move all
10243 // the others up
10244 m_cells.RemoveAt(startRow);
10245 }
10246
10247 m_rowCount = m_rowCount - noRows;
10248
10249 return true;
10250 }
10251
10252 bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10253 {
10254 wxASSERT((startCol + noCols) < m_colCount);
10255 if ((startCol + noCols) >= m_colCount)
10256 return false;
10257
10258 bool deleteRows = (noCols == m_colCount);
10259
10260 int i, j;
10261 for (i = 0; i < m_rowCount; i++)
10262 {
10263 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
10264 for (j = startCol; j < (startCol+noCols); j++)
10265 {
10266 wxRichTextObject* cell = colArray[j];
10267 RemoveChild(cell, true);
10268 }
10269
10270 if (deleteRows)
10271 m_cells.RemoveAt(0);
10272 }
10273
10274 if (deleteRows)
10275 m_rowCount = 0;
10276 m_colCount = m_colCount - noCols;
10277
10278 return true;
10279 }
10280
10281 bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10282 {
10283 wxASSERT(startRow <= m_rowCount);
10284 if (startRow > m_rowCount)
10285 return false;
10286
10287 int i, j;
10288 for (i = 0; i < noRows; i++)
10289 {
10290 int idx;
10291 if (startRow == m_rowCount)
10292 {
10293 m_cells.Add(wxRichTextObjectPtrArray());
10294 idx = m_cells.GetCount() - 1;
10295 }
10296 else
10297 {
10298 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10299 idx = startRow+i;
10300 }
10301
10302 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10303 for (j = 0; j < m_colCount; j++)
10304 {
10305 wxRichTextCell* cell = new wxRichTextCell;
10306 cell->GetAttributes() = attr;
10307
10308 AppendChild(cell);
10309 colArray.Add(cell);
10310 }
10311 }
10312
10313 m_rowCount = m_rowCount + noRows;
10314 return true;
10315 }
10316
10317 bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10318 {
10319 wxASSERT(startCol <= m_colCount);
10320 if (startCol > m_colCount)
10321 return false;
10322
10323 int i, j;
10324 for (i = 0; i < m_rowCount; i++)
10325 {
10326 wxRichTextObjectPtrArray& colArray = m_cells[i];
10327 for (j = 0; j < noCols; j++)
10328 {
10329 wxRichTextCell* cell = new wxRichTextCell;
10330 cell->GetAttributes() = attr;
10331
10332 AppendChild(cell);
10333
10334 if (startCol == m_colCount)
10335 colArray.Add(cell);
10336 else
10337 colArray.Insert(cell, startCol+j);
10338 }
10339 }
10340
10341 m_colCount = m_colCount + noCols;
10342
10343 return true;
10344 }
10345
10346 // Edit properties via a GUI
10347 bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10348 {
10349 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10350 boxDlg.SetAttributes(GetAttributes());
10351
10352 if (boxDlg.ShowModal() == wxID_OK)
10353 {
10354 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10355 return true;
10356 }
10357 else
10358 return false;
10359 }
10360
10361 /*
10362 * Module to initialise and clean up handlers
10363 */
10364
10365 class wxRichTextModule: public wxModule
10366 {
10367 DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10368 public:
10369 wxRichTextModule() {}
10370 bool OnInit()
10371 {
10372 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
10373 wxRichTextBuffer::InitStandardHandlers();
10374 wxRichTextParagraph::InitDefaultTabs();
10375
10376 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10377 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10378 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10379 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10380 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10381 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10382 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10385
10386 return true;
10387 }
10388 void OnExit()
10389 {
10390 wxRichTextBuffer::CleanUpHandlers();
10391 wxRichTextBuffer::CleanUpDrawingHandlers();
10392 wxRichTextBuffer::CleanUpFieldTypes();
10393 wxRichTextXMLHandler::ClearNodeToClassMap();
10394 wxRichTextDecimalToRoman(-1);
10395 wxRichTextParagraph::ClearDefaultTabs();
10396 wxRichTextCtrl::ClearAvailableFontNames();
10397 wxRichTextBuffer::SetRenderer(NULL);
10398 }
10399 };
10400
10401 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10402
10403
10404 // If the richtext lib is dynamically loaded after the app has already started
10405 // (such as from wxPython) then the built-in module system will not init this
10406 // module. Provide this function to do it manually.
10407 void wxRichTextModuleInit()
10408 {
10409 wxModule* module = new wxRichTextModule;
10410 module->Init();
10411 wxModule::RegisterModule(module);
10412 }
10413
10414
10415 /*!
10416 * Commands for undo/redo
10417 *
10418 */
10419
10420 wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
10421 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
10422 {
10423 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
10424 }
10425
10426 wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
10427 {
10428 }
10429
10430 wxRichTextCommand::~wxRichTextCommand()
10431 {
10432 ClearActions();
10433 }
10434
10435 void wxRichTextCommand::AddAction(wxRichTextAction* action)
10436 {
10437 if (!m_actions.Member(action))
10438 m_actions.Append(action);
10439 }
10440
10441 bool wxRichTextCommand::Do()
10442 {
10443 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
10444 {
10445 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10446 action->Do();
10447 }
10448
10449 return true;
10450 }
10451
10452 bool wxRichTextCommand::Undo()
10453 {
10454 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
10455 {
10456 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10457 action->Undo();
10458 }
10459
10460 return true;
10461 }
10462
10463 void wxRichTextCommand::ClearActions()
10464 {
10465 WX_CLEAR_LIST(wxList, m_actions);
10466 }
10467
10468 /*!
10469 * Individual action
10470 *
10471 */
10472
10473 wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10474 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10475 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
10476 {
10477 m_buffer = buffer;
10478 m_object = NULL;
10479 m_containerAddress.Create(buffer, container);
10480 m_ignoreThis = ignoreFirstTime;
10481 m_cmdId = id;
10482 m_position = -1;
10483 m_ctrl = ctrl;
10484 m_name = name;
10485 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10486 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10487 if (cmd)
10488 cmd->AddAction(this);
10489 }
10490
10491 wxRichTextAction::~wxRichTextAction()
10492 {
10493 if (m_object)
10494 delete m_object;
10495 }
10496
10497 // Returns the container that this action refers to, using the container address and top-level buffer.
10498 wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10499 {
10500 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10501 return container;
10502 }
10503
10504
10505 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10506 {
10507 // Store a list of line start character and y positions so we can figure out which area
10508 // we need to refresh
10509
10510 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10511 wxRichTextParagraphLayoutBox* container = GetContainer();
10512 wxASSERT(container != NULL);
10513 if (!container)
10514 return;
10515
10516 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10517 // If we had several actions, which only invalidate and leave layout until the
10518 // paint handler is called, then this might not be true. So we may need to switch
10519 // optimisation on only when we're simply adding text and not simultaneously
10520 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10521 // first, but of course this means we'll be doing it twice.
10522 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
10523 {
10524 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10525 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10526 int lastY = firstVisiblePt.y + clientSize.y;
10527
10528 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10529 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10530 while (node)
10531 {
10532 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10533 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10534 while (node2)
10535 {
10536 wxRichTextLine* line = node2->GetData();
10537 wxPoint pt = line->GetAbsolutePosition();
10538 wxRichTextRange range = line->GetAbsoluteRange();
10539
10540 if (pt.y > lastY)
10541 {
10542 node2 = wxRichTextLineList::compatibility_iterator();
10543 node = wxRichTextObjectList::compatibility_iterator();
10544 }
10545 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10546 {
10547 optimizationLineCharPositions.Add(range.GetStart());
10548 optimizationLineYPositions.Add(pt.y);
10549 }
10550
10551 if (node2)
10552 node2 = node2->GetNext();
10553 }
10554
10555 if (node)
10556 node = node->GetNext();
10557 }
10558 }
10559 #endif
10560 }
10561
10562 bool wxRichTextAction::Do()
10563 {
10564 m_buffer->Modify(true);
10565
10566 wxRichTextParagraphLayoutBox* container = GetContainer();
10567 wxASSERT(container != NULL);
10568 if (!container)
10569 return false;
10570
10571 switch (m_cmdId)
10572 {
10573 case wxRICHTEXT_INSERT:
10574 {
10575 // Store a list of line start character and y positions so we can figure out which area
10576 // we need to refresh
10577 wxArrayInt optimizationLineCharPositions;
10578 wxArrayInt optimizationLineYPositions;
10579
10580 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10581 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10582 #endif
10583
10584 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10585 container->UpdateRanges();
10586
10587 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10588 // Layout() would stop prematurely at the top level.
10589 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10590
10591 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
10592
10593 // Character position to caret position
10594 newCaretPosition --;
10595
10596 // Don't take into account the last newline
10597 if (m_newParagraphs.GetPartialParagraph())
10598 newCaretPosition --;
10599 else
10600 if (m_newParagraphs.GetChildren().GetCount() > 1)
10601 {
10602 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10603 if (p->GetRange().GetLength() == 1)
10604 newCaretPosition --;
10605 }
10606
10607 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
10608
10609 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10610
10611 wxRichTextEvent cmdEvent(
10612 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10613 m_ctrl ? m_ctrl->GetId() : -1);
10614 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10615 cmdEvent.SetRange(GetRange());
10616 cmdEvent.SetPosition(GetRange().GetStart());
10617 cmdEvent.SetContainer(container);
10618
10619 m_buffer->SendEvent(cmdEvent);
10620
10621 break;
10622 }
10623 case wxRICHTEXT_DELETE:
10624 {
10625 wxArrayInt optimizationLineCharPositions;
10626 wxArrayInt optimizationLineYPositions;
10627
10628 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10629 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10630 #endif
10631
10632 container->DeleteRange(GetRange());
10633 container->UpdateRanges();
10634 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10635 // Layout() would stop prematurely at the top level.
10636 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10637
10638 long caretPos = GetRange().GetStart()-1;
10639 if (caretPos >= container->GetOwnRange().GetEnd())
10640 caretPos --;
10641
10642 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10643
10644 wxRichTextEvent cmdEvent(
10645 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10646 m_ctrl ? m_ctrl->GetId() : -1);
10647 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10648 cmdEvent.SetRange(GetRange());
10649 cmdEvent.SetPosition(GetRange().GetStart());
10650 cmdEvent.SetContainer(container);
10651
10652 m_buffer->SendEvent(cmdEvent);
10653
10654 break;
10655 }
10656 case wxRICHTEXT_CHANGE_STYLE:
10657 case wxRICHTEXT_CHANGE_PROPERTIES:
10658 {
10659 ApplyParagraphs(GetNewParagraphs());
10660
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 {
10666 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10667 // Layout() would stop prematurely at the top level.
10668 container->InvalidateHierarchy(GetRange());
10669 }
10670
10671 UpdateAppearance(GetPosition());
10672
10673 wxRichTextEvent cmdEvent(
10674 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10675 m_ctrl ? m_ctrl->GetId() : -1);
10676 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10677 cmdEvent.SetRange(GetRange());
10678 cmdEvent.SetPosition(GetRange().GetStart());
10679 cmdEvent.SetContainer(container);
10680
10681 m_buffer->SendEvent(cmdEvent);
10682
10683 break;
10684 }
10685 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10686 {
10687 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10688 if (obj)
10689 {
10690 wxRichTextAttr oldAttr = obj->GetAttributes();
10691 obj->GetAttributes() = m_attributes;
10692 m_attributes = oldAttr;
10693 }
10694
10695 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10696 // Layout() would stop prematurely at the top level.
10697 // Invalidate the whole buffer if there were floating objects
10698 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10699 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10700 else
10701 container->InvalidateHierarchy(GetRange());
10702
10703 UpdateAppearance(GetPosition());
10704
10705 wxRichTextEvent cmdEvent(
10706 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10707 m_ctrl ? m_ctrl->GetId() : -1);
10708 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10709 cmdEvent.SetRange(GetRange());
10710 cmdEvent.SetPosition(GetRange().GetStart());
10711 cmdEvent.SetContainer(container);
10712
10713 m_buffer->SendEvent(cmdEvent);
10714
10715 break;
10716 }
10717 case wxRICHTEXT_CHANGE_OBJECT:
10718 {
10719 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10720 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10721 if (obj && m_object)
10722 {
10723 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10724 if (node)
10725 {
10726 wxRichTextObject* obj = node->GetData();
10727 node->SetData(m_object);
10728 m_object = obj;
10729 }
10730 }
10731
10732 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10733 // Layout() would stop prematurely at the top level.
10734 // Invalidate the whole buffer if there were floating objects
10735 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10736 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10737 else
10738 container->InvalidateHierarchy(GetRange());
10739
10740 UpdateAppearance(GetPosition());
10741
10742 // TODO: send new kind of modification event
10743
10744 break;
10745 }
10746 default:
10747 break;
10748 }
10749
10750 return true;
10751 }
10752
10753 bool wxRichTextAction::Undo()
10754 {
10755 m_buffer->Modify(true);
10756
10757 wxRichTextParagraphLayoutBox* container = GetContainer();
10758 wxASSERT(container != NULL);
10759 if (!container)
10760 return false;
10761
10762 switch (m_cmdId)
10763 {
10764 case wxRICHTEXT_INSERT:
10765 {
10766 wxArrayInt optimizationLineCharPositions;
10767 wxArrayInt optimizationLineYPositions;
10768
10769 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10770 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10771 #endif
10772
10773 container->DeleteRange(GetRange());
10774 container->UpdateRanges();
10775
10776 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10777 // Layout() would stop prematurely at the top level.
10778 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10779
10780 long newCaretPosition = GetPosition() - 1;
10781
10782 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10783
10784 wxRichTextEvent cmdEvent(
10785 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10786 m_ctrl ? m_ctrl->GetId() : -1);
10787 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10788 cmdEvent.SetRange(GetRange());
10789 cmdEvent.SetPosition(GetRange().GetStart());
10790 cmdEvent.SetContainer(container);
10791
10792 m_buffer->SendEvent(cmdEvent);
10793
10794 break;
10795 }
10796 case wxRICHTEXT_DELETE:
10797 {
10798 wxArrayInt optimizationLineCharPositions;
10799 wxArrayInt optimizationLineYPositions;
10800
10801 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10802 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10803 #endif
10804
10805 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10806 container->UpdateRanges();
10807
10808 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10809 // Layout() would stop prematurely at the top level.
10810 container->InvalidateHierarchy(GetRange());
10811
10812 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10813
10814 wxRichTextEvent cmdEvent(
10815 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10816 m_ctrl ? m_ctrl->GetId() : -1);
10817 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10818 cmdEvent.SetRange(GetRange());
10819 cmdEvent.SetPosition(GetRange().GetStart());
10820 cmdEvent.SetContainer(container);
10821
10822 m_buffer->SendEvent(cmdEvent);
10823
10824 break;
10825 }
10826 case wxRICHTEXT_CHANGE_STYLE:
10827 case wxRICHTEXT_CHANGE_PROPERTIES:
10828 {
10829 ApplyParagraphs(GetOldParagraphs());
10830 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10831 // Layout() would stop prematurely at the top level.
10832 container->InvalidateHierarchy(GetRange());
10833
10834 UpdateAppearance(GetPosition());
10835
10836 wxRichTextEvent cmdEvent(
10837 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10838 m_ctrl ? m_ctrl->GetId() : -1);
10839 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10840 cmdEvent.SetRange(GetRange());
10841 cmdEvent.SetPosition(GetRange().GetStart());
10842 cmdEvent.SetContainer(container);
10843
10844 m_buffer->SendEvent(cmdEvent);
10845
10846 break;
10847 }
10848 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10849 case wxRICHTEXT_CHANGE_OBJECT:
10850 {
10851 return Do();
10852 }
10853 default:
10854 break;
10855 }
10856
10857 return true;
10858 }
10859
10860 /// Update the control appearance
10861 void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
10862 {
10863 wxRichTextParagraphLayoutBox* container = GetContainer();
10864 wxASSERT(container != NULL);
10865 if (!container)
10866 return;
10867
10868 if (m_ctrl)
10869 {
10870 m_ctrl->SetFocusObject(container);
10871 m_ctrl->SetCaretPosition(caretPosition);
10872
10873 if (!m_ctrl->IsFrozen())
10874 {
10875 wxRect containerRect = container->GetRect();
10876
10877 m_ctrl->LayoutContent();
10878
10879 // Refresh everything if there were floating objects or the container changed size
10880 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10881 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
10882 {
10883 m_ctrl->Refresh(false);
10884 }
10885 else
10886
10887 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10888 // Find refresh rectangle if we are in a position to optimise refresh
10889 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10890 {
10891 size_t i;
10892
10893 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10894 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10895
10896 // Start/end positions
10897 int firstY = 0;
10898 int lastY = firstVisiblePt.y + clientSize.y;
10899
10900 bool foundEnd = false;
10901
10902 // position offset - how many characters were inserted
10903 int positionOffset = GetRange().GetLength();
10904
10905 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10906 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10907 positionOffset = - positionOffset;
10908
10909 // find the first line which is being drawn at the same position as it was
10910 // before. Since we're talking about a simple insertion, we can assume
10911 // that the rest of the window does not need to be redrawn.
10912
10913 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10914 // Since we support floating layout, we should redraw the whole para instead of just
10915 // the first line touching the invalid range.
10916 if (para)
10917 {
10918 firstY = para->GetPosition().y;
10919 }
10920
10921 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10922 while (node)
10923 {
10924 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10925 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10926 while (node2)
10927 {
10928 wxRichTextLine* line = node2->GetData();
10929 wxPoint pt = line->GetAbsolutePosition();
10930 wxRichTextRange range = line->GetAbsoluteRange();
10931
10932 // we want to find the first line that is in the same position
10933 // as before. This will mean we're at the end of the changed text.
10934
10935 if (pt.y > lastY) // going past the end of the window, no more info
10936 {
10937 node2 = wxRichTextLineList::compatibility_iterator();
10938 node = wxRichTextObjectList::compatibility_iterator();
10939 }
10940 // Detect last line in the buffer
10941 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10942 {
10943 // If deleting text, make sure we refresh below as well as above
10944 if (positionOffset >= 0)
10945 {
10946 foundEnd = true;
10947 lastY = pt.y + line->GetSize().y;
10948 }
10949
10950 node2 = wxRichTextLineList::compatibility_iterator();
10951 node = wxRichTextObjectList::compatibility_iterator();
10952
10953 break;
10954 }
10955 else
10956 {
10957 // search for this line being at the same position as before
10958 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10959 {
10960 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10961 ((*optimizationLineYPositions)[i] == pt.y))
10962 {
10963 // Stop, we're now the same as we were
10964 foundEnd = true;
10965
10966 lastY = pt.y;
10967
10968 node2 = wxRichTextLineList::compatibility_iterator();
10969 node = wxRichTextObjectList::compatibility_iterator();
10970
10971 break;
10972 }
10973 }
10974 }
10975
10976 if (node2)
10977 node2 = node2->GetNext();
10978 }
10979
10980 if (node)
10981 node = node->GetNext();
10982 }
10983
10984 firstY = wxMax(firstVisiblePt.y, firstY);
10985 if (!foundEnd)
10986 lastY = firstVisiblePt.y + clientSize.y;
10987
10988 // Convert to device coordinates
10989 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
10990 m_ctrl->RefreshRect(rect);
10991 }
10992 else
10993 #endif
10994 m_ctrl->Refresh(false);
10995
10996 m_ctrl->PositionCaret();
10997
10998 // This causes styles to persist when doing programmatic
10999 // content creation except when Freeze/Thaw is used, so
11000 // disable this and check for the consequences.
11001 // m_ctrl->SetDefaultStyleToCursorStyle();
11002
11003 if (sendUpdateEvent)
11004 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
11005 }
11006 }
11007 }
11008
11009 /// Replace the buffer paragraphs with the new ones.
11010 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
11011 {
11012 wxRichTextParagraphLayoutBox* container = GetContainer();
11013 wxASSERT(container != NULL);
11014 if (!container)
11015 return;
11016
11017 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
11018 while (node)
11019 {
11020 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
11021 wxASSERT (para != NULL);
11022
11023 // We'll replace the existing paragraph by finding the paragraph at this position,
11024 // delete its node data, and setting a copy as the new node data.
11025 // TODO: make more efficient by simply swapping old and new paragraph objects.
11026
11027 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
11028 if (existingPara)
11029 {
11030 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
11031 if (bufferParaNode)
11032 {
11033 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
11034 newPara->SetParent(container);
11035
11036 bufferParaNode->SetData(newPara);
11037
11038 delete existingPara;
11039 }
11040 }
11041
11042 node = node->GetNext();
11043 }
11044 }
11045
11046
11047 /*!
11048 * wxRichTextRange
11049 * This stores beginning and end positions for a range of data.
11050 */
11051
11052 WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11053
11054 /// Limit this range to be within 'range'
11055 bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11056 {
11057 if (m_start < range.m_start)
11058 m_start = range.m_start;
11059
11060 if (m_end > range.m_end)
11061 m_end = range.m_end;
11062
11063 return true;
11064 }
11065
11066 /*!
11067 * wxRichTextImage implementation
11068 * This object represents an image.
11069 */
11070
11071 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
11072
11073 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11074 wxRichTextObject(parent)
11075 {
11076 Init();
11077 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
11078 if (charStyle)
11079 SetAttributes(*charStyle);
11080 }
11081
11082 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11083 wxRichTextObject(parent)
11084 {
11085 Init();
11086 m_imageBlock = imageBlock;
11087 if (charStyle)
11088 SetAttributes(*charStyle);
11089 }
11090
11091 wxRichTextImage::~wxRichTextImage()
11092 {
11093 }
11094
11095 void wxRichTextImage::Init()
11096 {
11097 m_originalImageSize = wxSize(-1, -1);
11098 }
11099
11100 /// Create a cached image at the required size
11101 bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
11102 {
11103 if (!m_imageBlock.IsOk())
11104 return false;
11105
11106 // If we have an original image size, use that to compute the cached bitmap size
11107 // instead of loading the image each time. This way we can avoid loading
11108 // the image so long as the new cached bitmap size hasn't changed.
11109
11110 wxImage image;
11111 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
11112 {
11113 m_imageCache = wxNullBitmap;
11114
11115 m_imageBlock.Load(image);
11116 if (!image.IsOk())
11117 return false;
11118
11119 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11120 }
11121
11122 int width = m_originalImageSize.GetWidth();
11123 int height = m_originalImageSize.GetHeight();
11124
11125 int parentWidth = 0;
11126 int parentHeight = 0;
11127
11128 int maxWidth = -1;
11129 int maxHeight = -1;
11130
11131 wxSize sz = parentSize;
11132 if (sz == wxDefaultSize)
11133 {
11134 if (GetParent() && GetParent()->GetParent())
11135 sz = GetParent()->GetParent()->GetCachedSize();
11136 }
11137
11138 if (sz != wxDefaultSize)
11139 {
11140 wxRichTextBuffer* buffer = GetBuffer();
11141 if (buffer)
11142 {
11143 // Find the actual space available when margin is taken into account
11144 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11145 marginRect = wxRect(0, 0, sz.x, sz.y);
11146 if (GetParent() && GetParent()->GetParent())
11147 {
11148 buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11149 sz = contentRect.GetSize();
11150 }
11151
11152 // Use a minimum size to stop images becoming very small
11153 parentWidth = wxMax(100, sz.GetWidth());
11154 parentHeight = wxMax(100, sz.GetHeight());
11155
11156 if (buffer->GetRichTextCtrl())
11157 // Start with a maximum width of the control size, even if not specified by the content,
11158 // to minimize the amount of picture overlapping the right-hand side
11159 maxWidth = parentWidth;
11160 }
11161 }
11162
11163 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11164 {
11165 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11166 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11167 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11168 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11169 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11170 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11171 }
11172
11173 // Limit to max width
11174
11175 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11176 {
11177 int mw = -1;
11178
11179 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11180 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11181 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11182 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11183 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11184 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11185
11186 // If we already have a smaller max width due to the constraints of the control size,
11187 // don't use the larger max width.
11188 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11189 maxWidth = mw;
11190 }
11191
11192 if (maxWidth > 0 && width > maxWidth)
11193 width = maxWidth;
11194
11195 // Preserve the aspect ratio
11196 if (width != m_originalImageSize.GetWidth())
11197 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11198
11199 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11200 {
11201 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11202 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11203 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11204 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11205 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11206 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11207
11208 // Preserve the aspect ratio
11209 if (height != m_originalImageSize.GetHeight())
11210 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11211 }
11212
11213 // Limit to max height
11214
11215 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11216 {
11217 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11218 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11219 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11220 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11221 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11222 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11223 }
11224
11225 if (maxHeight > 0 && height > maxHeight)
11226 {
11227 height = maxHeight;
11228
11229 // Preserve the aspect ratio
11230 if (height != m_originalImageSize.GetHeight())
11231 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11232 }
11233
11234 // Prevent the use of zero size
11235 width = wxMax(1, width);
11236 height = wxMax(1, height);
11237
11238 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11239 {
11240 // Do nothing, we didn't need to change the image cache
11241 }
11242 else
11243 {
11244 if (!image.IsOk())
11245 {
11246 m_imageBlock.Load(image);
11247 if (!image.IsOk())
11248 return false;
11249 }
11250
11251 if (image.GetWidth() == width && image.GetHeight() == height)
11252 m_imageCache = wxBitmap(image);
11253 else
11254 {
11255 // If the original width and height is small, e.g. 400 or below,
11256 // scale up and then down to improve image quality. This can make
11257 // a big difference, with not much performance hit.
11258 int upscaleThreshold = 400;
11259 wxImage img;
11260 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11261 {
11262 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11263 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11264 }
11265 else
11266 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11267 m_imageCache = wxBitmap(img);
11268 }
11269 }
11270
11271 return m_imageCache.IsOk();
11272 }
11273
11274 /// Draw the item
11275 bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
11276 {
11277 if (!IsShown())
11278 return true;
11279
11280 // Don't need cached size AFAIK
11281 // wxSize size = GetCachedSize();
11282 if (!LoadImageCache(dc))
11283 return false;
11284
11285 wxRichTextAttr attr(GetAttributes());
11286 context.ApplyVirtualAttributes(attr, this);
11287
11288 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
11289
11290 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11291 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11292 marginRect = rect; // outer rectangle, will calculate contentRect
11293 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11294
11295 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
11296
11297 if (selection.WithinSelection(GetRange().GetStart(), this))
11298 {
11299 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11300 wxCheckSetPen(dc, *wxBLACK_PEN);
11301 dc.SetLogicalFunction(wxINVERT);
11302 dc.DrawRectangle(contentRect);
11303 dc.SetLogicalFunction(wxCOPY);
11304 }
11305
11306 return true;
11307 }
11308
11309 /// Lay the item out
11310 bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
11311 {
11312 if (!LoadImageCache(dc))
11313 return false;
11314
11315 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11316 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11317 contentRect = wxRect(wxPoint(0,0), imageSize);
11318
11319 wxRichTextAttr attr(GetAttributes());
11320 context.ApplyVirtualAttributes(attr, this);
11321
11322 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11323
11324 wxSize overallSize = marginRect.GetSize();
11325
11326 SetCachedSize(overallSize);
11327 SetMaxSize(overallSize);
11328 SetMinSize(overallSize);
11329 SetPosition(rect.GetPosition());
11330
11331 return true;
11332 }
11333
11334 /// Get/set the object size for the given range. Returns false if the range
11335 /// is invalid for this object.
11336 bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), const wxPoint& WXUNUSED(position), const wxSize& parentSize, wxArrayInt* partialExtents) const
11337 {
11338 if (!range.IsWithin(GetRange()))
11339 return false;
11340
11341 if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
11342 {
11343 size.x = 0; size.y = 0;
11344 if (partialExtents)
11345 partialExtents->Add(0);
11346 return false;
11347 }
11348
11349 wxRichTextAttr attr(GetAttributes());
11350 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11351
11352 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11353 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11354 contentRect = wxRect(wxPoint(0,0), imageSize);
11355 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11356
11357 wxSize overallSize = marginRect.GetSize();
11358
11359 if (partialExtents)
11360 partialExtents->Add(overallSize.x);
11361
11362 size = overallSize;
11363
11364 return true;
11365 }
11366
11367 // Get the 'natural' size for an object. For an image, it would be the
11368 // image size.
11369 wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11370 {
11371 wxTextAttrSize size;
11372 if (GetImageCache().IsOk())
11373 {
11374 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11375 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11376 }
11377 return size;
11378 }
11379
11380
11381 /// Copy
11382 void wxRichTextImage::Copy(const wxRichTextImage& obj)
11383 {
11384 wxRichTextObject::Copy(obj);
11385
11386 m_imageBlock = obj.m_imageBlock;
11387 m_originalImageSize = obj.m_originalImageSize;
11388 }
11389
11390 /// Edit properties via a GUI
11391 bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11392 {
11393 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11394 imageDlg.SetAttributes(GetAttributes());
11395
11396 if (imageDlg.ShowModal() == wxID_OK)
11397 {
11398 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11399 // indeterminate in the object.
11400 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
11401 return true;
11402 }
11403 else
11404 return false;
11405 }
11406
11407 /*!
11408 * Utilities
11409 *
11410 */
11411
11412 /// Compare two attribute objects
11413 bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
11414 {
11415 return (attr1 == attr2);
11416 }
11417
11418 /// Compare tabs
11419 bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11420 {
11421 if (tabs1.GetCount() != tabs2.GetCount())
11422 return false;
11423
11424 size_t i;
11425 for (i = 0; i < tabs1.GetCount(); i++)
11426 {
11427 if (tabs1[i] != tabs2[i])
11428 return false;
11429 }
11430 return true;
11431 }
11432
11433 bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
11434 {
11435 return destStyle.Apply(style, compareWith);
11436 }
11437
11438 // Remove attributes
11439 bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
11440 {
11441 return destStyle.RemoveStyle(style);
11442 }
11443
11444 /// Combine two bitlists, specifying the bits of interest with separate flags.
11445 bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11446 {
11447 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
11448 }
11449
11450 /// Compare two bitlists
11451 bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11452 {
11453 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
11454 }
11455
11456 /// Split into paragraph and character styles
11457 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
11458 {
11459 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
11460 }
11461
11462 /// Convert a decimal to Roman numerals
11463 wxString wxRichTextDecimalToRoman(long n)
11464 {
11465 static wxArrayInt decimalNumbers;
11466 static wxArrayString romanNumbers;
11467
11468 // Clean up arrays
11469 if (n == -1)
11470 {
11471 decimalNumbers.Clear();
11472 romanNumbers.Clear();
11473 return wxEmptyString;
11474 }
11475
11476 if (decimalNumbers.GetCount() == 0)
11477 {
11478 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11479
11480 wxRichTextAddDecRom(1000, wxT("M"));
11481 wxRichTextAddDecRom(900, wxT("CM"));
11482 wxRichTextAddDecRom(500, wxT("D"));
11483 wxRichTextAddDecRom(400, wxT("CD"));
11484 wxRichTextAddDecRom(100, wxT("C"));
11485 wxRichTextAddDecRom(90, wxT("XC"));
11486 wxRichTextAddDecRom(50, wxT("L"));
11487 wxRichTextAddDecRom(40, wxT("XL"));
11488 wxRichTextAddDecRom(10, wxT("X"));
11489 wxRichTextAddDecRom(9, wxT("IX"));
11490 wxRichTextAddDecRom(5, wxT("V"));
11491 wxRichTextAddDecRom(4, wxT("IV"));
11492 wxRichTextAddDecRom(1, wxT("I"));
11493 }
11494
11495 int i = 0;
11496 wxString roman;
11497
11498 while (n > 0 && i < 13)
11499 {
11500 if (n >= decimalNumbers[i])
11501 {
11502 n -= decimalNumbers[i];
11503 roman += romanNumbers[i];
11504 }
11505 else
11506 {
11507 i ++;
11508 }
11509 }
11510 if (roman.IsEmpty())
11511 roman = wxT("0");
11512 return roman;
11513 }
11514
11515 /*!
11516 * wxRichTextFileHandler
11517 * Base class for file handlers
11518 */
11519
11520 IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
11521
11522 #if wxUSE_FFILE && wxUSE_STREAMS
11523 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
11524 {
11525 wxFFileInputStream stream(filename);
11526 if (stream.IsOk())
11527 return LoadFile(buffer, stream);
11528
11529 return false;
11530 }
11531
11532 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11533 {
11534 wxFFileOutputStream stream(filename);
11535 if (stream.IsOk())
11536 return SaveFile(buffer, stream);
11537
11538 return false;
11539 }
11540 #endif // wxUSE_FFILE && wxUSE_STREAMS
11541
11542 /// Can we handle this filename (if using files)? By default, checks the extension.
11543 bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11544 {
11545 wxString path, file, ext;
11546 wxFileName::SplitPath(filename, & path, & file, & ext);
11547
11548 return (ext.Lower() == GetExtension());
11549 }
11550
11551 /*!
11552 * wxRichTextTextHandler
11553 * Plain text handler
11554 */
11555
11556 IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
11557
11558 #if wxUSE_STREAMS
11559 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11560 {
11561 if (!stream.IsOk())
11562 return false;
11563
11564 wxString str;
11565 int lastCh = 0;
11566
11567 while (!stream.Eof())
11568 {
11569 int ch = stream.GetC();
11570
11571 if (!stream.Eof())
11572 {
11573 if (ch == 10 && lastCh != 13)
11574 str += wxT('\n');
11575
11576 if (ch > 0 && ch != 10)
11577 str += wxChar(ch);
11578
11579 lastCh = ch;
11580 }
11581 }
11582
11583 buffer->ResetAndClearCommands();
11584 buffer->Clear();
11585 buffer->AddParagraphs(str);
11586 buffer->UpdateRanges();
11587
11588 return true;
11589 }
11590
11591 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11592 {
11593 if (!stream.IsOk())
11594 return false;
11595
11596 wxString text = buffer->GetText();
11597
11598 wxString newLine = wxRichTextLineBreakChar;
11599 text.Replace(newLine, wxT("\n"));
11600
11601 wxCharBuffer buf = text.ToAscii();
11602
11603 stream.Write((const char*) buf, text.length());
11604 return true;
11605 }
11606 #endif // wxUSE_STREAMS
11607
11608 /*
11609 * Stores information about an image, in binary in-memory form
11610 */
11611
11612 wxRichTextImageBlock::wxRichTextImageBlock()
11613 {
11614 Init();
11615 }
11616
11617 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11618 {
11619 Init();
11620 Copy(block);
11621 }
11622
11623 wxRichTextImageBlock::~wxRichTextImageBlock()
11624 {
11625 wxDELETEA(m_data);
11626 }
11627
11628 void wxRichTextImageBlock::Init()
11629 {
11630 m_data = NULL;
11631 m_dataSize = 0;
11632 m_imageType = wxBITMAP_TYPE_INVALID;
11633 }
11634
11635 void wxRichTextImageBlock::Clear()
11636 {
11637 wxDELETEA(m_data);
11638 m_dataSize = 0;
11639 m_imageType = wxBITMAP_TYPE_INVALID;
11640 }
11641
11642
11643 // Load the original image into a memory block.
11644 // If the image is not a JPEG, we must convert it into a JPEG
11645 // to conserve space.
11646 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11647 // load the image a 2nd time.
11648
11649 bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11650 wxImage& image, bool convertToJPEG)
11651 {
11652 m_imageType = imageType;
11653
11654 wxString filenameToRead(filename);
11655 bool removeFile = false;
11656
11657 if (imageType == wxBITMAP_TYPE_INVALID)
11658 return false; // Could not determine image type
11659
11660 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11661 {
11662 wxString tempFile =
11663 wxFileName::CreateTempFileName(_("image"));
11664
11665 wxASSERT(!tempFile.IsEmpty());
11666
11667 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11668 filenameToRead = tempFile;
11669 removeFile = true;
11670
11671 m_imageType = wxBITMAP_TYPE_JPEG;
11672 }
11673 wxFile file;
11674 if (!file.Open(filenameToRead))
11675 return false;
11676
11677 m_dataSize = (size_t) file.Length();
11678 file.Close();
11679
11680 if (m_data)
11681 delete[] m_data;
11682 m_data = ReadBlock(filenameToRead, m_dataSize);
11683
11684 if (removeFile)
11685 wxRemoveFile(filenameToRead);
11686
11687 return (m_data != NULL);
11688 }
11689
11690 // Make an image block from the wxImage in the given
11691 // format.
11692 bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
11693 {
11694 image.SetOption(wxT("quality"), quality);
11695
11696 if (imageType == wxBITMAP_TYPE_INVALID)
11697 return false; // Could not determine image type
11698
11699 return DoMakeImageBlock(image, imageType);
11700 }
11701
11702 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11703 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11704 {
11705 if (imageType == wxBITMAP_TYPE_INVALID)
11706 return false; // Could not determine image type
11707
11708 return DoMakeImageBlock(image, imageType);
11709 }
11710
11711 // Makes the image block
11712 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11713 {
11714 wxMemoryOutputStream memStream;
11715 if (!image.SaveFile(memStream, imageType))
11716 {
11717 return false;
11718 }
11719
11720 unsigned char* block = new unsigned char[memStream.GetSize()];
11721 if (!block)
11722 return false;
11723
11724 if (m_data)
11725 delete[] m_data;
11726 m_data = block;
11727
11728 m_imageType = imageType;
11729 m_dataSize = memStream.GetSize();
11730
11731 memStream.CopyTo(m_data, m_dataSize);
11732
11733 return (m_data != NULL);
11734 }
11735
11736 // Write to a file
11737 bool wxRichTextImageBlock::Write(const wxString& filename)
11738 {
11739 return WriteBlock(filename, m_data, m_dataSize);
11740 }
11741
11742 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11743 {
11744 m_imageType = block.m_imageType;
11745 wxDELETEA(m_data);
11746 m_dataSize = block.m_dataSize;
11747 if (m_dataSize == 0)
11748 return;
11749
11750 m_data = new unsigned char[m_dataSize];
11751 unsigned int i;
11752 for (i = 0; i < m_dataSize; i++)
11753 m_data[i] = block.m_data[i];
11754 }
11755
11756 //// Operators
11757 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11758 {
11759 Copy(block);
11760 }
11761
11762 // Load a wxImage from the block
11763 bool wxRichTextImageBlock::Load(wxImage& image)
11764 {
11765 if (!m_data)
11766 return false;
11767
11768 // Read in the image.
11769 #if wxUSE_STREAMS
11770 wxMemoryInputStream mstream(m_data, m_dataSize);
11771 bool success = image.LoadFile(mstream, GetImageType());
11772 #else
11773 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11774 wxASSERT(!tempFile.IsEmpty());
11775
11776 if (!WriteBlock(tempFile, m_data, m_dataSize))
11777 {
11778 return false;
11779 }
11780 success = image.LoadFile(tempFile, GetImageType());
11781 wxRemoveFile(tempFile);
11782 #endif
11783
11784 return success;
11785 }
11786
11787 // Write data in hex to a stream
11788 bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11789 {
11790 if (m_dataSize == 0)
11791 return true;
11792
11793 int bufSize = 100000;
11794 if (int(2*m_dataSize) < bufSize)
11795 bufSize = 2*m_dataSize;
11796 char* buf = new char[bufSize+1];
11797
11798 int left = m_dataSize;
11799 int n, i, j;
11800 j = 0;
11801 while (left > 0)
11802 {
11803 if (left*2 > bufSize)
11804 {
11805 n = bufSize; left -= (bufSize/2);
11806 }
11807 else
11808 {
11809 n = left*2; left = 0;
11810 }
11811
11812 char* b = buf;
11813 for (i = 0; i < (n/2); i++)
11814 {
11815 wxDecToHex(m_data[j], b, b+1);
11816 b += 2; j ++;
11817 }
11818
11819 buf[n] = 0;
11820 stream.Write((const char*) buf, n);
11821 }
11822 delete[] buf;
11823 return true;
11824 }
11825
11826 // Read data in hex from a stream
11827 bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
11828 {
11829 int dataSize = length/2;
11830
11831 if (m_data)
11832 delete[] m_data;
11833
11834 // create a null terminated temporary string:
11835 char str[3];
11836 str[2] = '\0';
11837
11838 m_data = new unsigned char[dataSize];
11839 int i;
11840 for (i = 0; i < dataSize; i ++)
11841 {
11842 str[0] = (char)stream.GetC();
11843 str[1] = (char)stream.GetC();
11844
11845 m_data[i] = (unsigned char)wxHexToDec(str);
11846 }
11847
11848 m_dataSize = dataSize;
11849 m_imageType = imageType;
11850
11851 return true;
11852 }
11853
11854 // Allocate and read from stream as a block of memory
11855 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11856 {
11857 unsigned char* block = new unsigned char[size];
11858 if (!block)
11859 return NULL;
11860
11861 stream.Read(block, size);
11862
11863 return block;
11864 }
11865
11866 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11867 {
11868 wxFileInputStream stream(filename);
11869 if (!stream.IsOk())
11870 return NULL;
11871
11872 return ReadBlock(stream, size);
11873 }
11874
11875 // Write memory block to stream
11876 bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11877 {
11878 stream.Write((void*) block, size);
11879 return stream.IsOk();
11880
11881 }
11882
11883 // Write memory block to file
11884 bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11885 {
11886 wxFileOutputStream outStream(filename);
11887 if (!outStream.IsOk())
11888 return false;
11889
11890 return WriteBlock(outStream, block, size);
11891 }
11892
11893 // Gets the extension for the block's type
11894 wxString wxRichTextImageBlock::GetExtension() const
11895 {
11896 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11897 if (handler)
11898 return handler->GetExtension();
11899 else
11900 return wxEmptyString;
11901 }
11902
11903 #if wxUSE_DATAOBJ
11904
11905 /*!
11906 * The data object for a wxRichTextBuffer
11907 */
11908
11909 const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
11910
11911 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11912 {
11913 m_richTextBuffer = richTextBuffer;
11914
11915 // this string should uniquely identify our format, but is otherwise
11916 // arbitrary
11917 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11918
11919 SetFormat(m_formatRichTextBuffer);
11920 }
11921
11922 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11923 {
11924 delete m_richTextBuffer;
11925 }
11926
11927 // after a call to this function, the richTextBuffer is owned by the caller and it
11928 // is responsible for deleting it!
11929 wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11930 {
11931 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11932 m_richTextBuffer = NULL;
11933
11934 return richTextBuffer;
11935 }
11936
11937 wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11938 {
11939 return m_formatRichTextBuffer;
11940 }
11941
11942 size_t wxRichTextBufferDataObject::GetDataSize() const
11943 {
11944 if (!m_richTextBuffer)
11945 return 0;
11946
11947 wxString bufXML;
11948
11949 {
11950 wxStringOutputStream stream(& bufXML);
11951 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11952 {
11953 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11954 return 0;
11955 }
11956 }
11957
11958 #if wxUSE_UNICODE
11959 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11960 return strlen(buffer) + 1;
11961 #else
11962 return bufXML.Length()+1;
11963 #endif
11964 }
11965
11966 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11967 {
11968 if (!pBuf || !m_richTextBuffer)
11969 return false;
11970
11971 wxString bufXML;
11972
11973 {
11974 wxStringOutputStream stream(& bufXML);
11975 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11976 {
11977 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11978 return 0;
11979 }
11980 }
11981
11982 #if wxUSE_UNICODE
11983 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11984 size_t len = strlen(buffer);
11985 memcpy((char*) pBuf, (const char*) buffer, len);
11986 ((char*) pBuf)[len] = 0;
11987 #else
11988 size_t len = bufXML.Length();
11989 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11990 ((char*) pBuf)[len] = 0;
11991 #endif
11992
11993 return true;
11994 }
11995
11996 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
11997 {
11998 wxDELETE(m_richTextBuffer);
11999
12000 wxString bufXML((const char*) buf, wxConvUTF8);
12001
12002 m_richTextBuffer = new wxRichTextBuffer;
12003
12004 wxStringInputStream stream(bufXML);
12005 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
12006 {
12007 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12008
12009 wxDELETE(m_richTextBuffer);
12010
12011 return false;
12012 }
12013 return true;
12014 }
12015
12016 #endif
12017 // wxUSE_DATAOBJ
12018
12019
12020 /*
12021 * wxRichTextFontTable
12022 * Manages quick access to a pool of fonts for rendering rich text
12023 */
12024
12025 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
12026
12027 class wxRichTextFontTableData: public wxObjectRefData
12028 {
12029 public:
12030 wxRichTextFontTableData() {}
12031
12032 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
12033
12034 wxRichTextFontTableHashMap m_hashMap;
12035 };
12036
12037 wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
12038 {
12039 wxString facename(fontSpec.GetFontFaceName());
12040
12041 int fontSize = fontSpec.GetFontSize();
12042 if (fontScale != 1.0)
12043 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12044
12045 wxString units;
12046 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12047 units = wxT("px");
12048 else
12049 units = wxT("pt");
12050 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12051 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12052 facename.c_str(), (int) fontSpec.GetFontEncoding());
12053
12054 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
12055 if ( entry == m_hashMap.end() )
12056 {
12057 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12058 {
12059 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
12060 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12061 font.SetStrikethrough(true);
12062 m_hashMap[spec] = font;
12063 return font;
12064 }
12065 else
12066 {
12067 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
12068 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12069 font.SetStrikethrough(true);
12070
12071 m_hashMap[spec] = font;
12072 return font;
12073 }
12074 }
12075 else
12076 {
12077 return entry->second;
12078 }
12079 }
12080
12081 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12082
12083 wxRichTextFontTable::wxRichTextFontTable()
12084 {
12085 m_refData = new wxRichTextFontTableData;
12086 m_fontScale = 1.0;
12087 }
12088
12089 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
12090 : wxObject()
12091 {
12092 (*this) = table;
12093 }
12094
12095 wxRichTextFontTable::~wxRichTextFontTable()
12096 {
12097 UnRef();
12098 }
12099
12100 bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12101 {
12102 return (m_refData == table.m_refData);
12103 }
12104
12105 void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12106 {
12107 Ref(table);
12108 m_fontScale = table.m_fontScale;
12109 }
12110
12111 wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
12112 {
12113 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12114 if (data)
12115 return data->FindFont(fontSpec, m_fontScale);
12116 else
12117 return wxFont();
12118 }
12119
12120 void wxRichTextFontTable::Clear()
12121 {
12122 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12123 if (data)
12124 data->m_hashMap.clear();
12125 }
12126
12127 void wxRichTextFontTable::SetFontScale(double fontScale)
12128 {
12129 if (fontScale != m_fontScale)
12130 Clear();
12131 m_fontScale = fontScale;
12132 }
12133
12134 // wxTextBoxAttr
12135
12136 void wxTextBoxAttr::Reset()
12137 {
12138 m_flags = 0;
12139 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12140 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12141 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12142 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
12143 m_boxStyleName = wxEmptyString;
12144
12145 m_margins.Reset();
12146 m_padding.Reset();
12147 m_position.Reset();
12148
12149 m_size.Reset();
12150 m_minSize.Reset();
12151 m_maxSize.Reset();
12152
12153 m_border.Reset();
12154 m_outline.Reset();
12155 }
12156
12157 // Equality test
12158 bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12159 {
12160 return (
12161 m_flags == attr.m_flags &&
12162 m_floatMode == attr.m_floatMode &&
12163 m_clearMode == attr.m_clearMode &&
12164 m_collapseMode == attr.m_collapseMode &&
12165 m_verticalAlignment == attr.m_verticalAlignment &&
12166
12167 m_margins == attr.m_margins &&
12168 m_padding == attr.m_padding &&
12169 m_position == attr.m_position &&
12170
12171 m_size == attr.m_size &&
12172 m_minSize == attr.m_minSize &&
12173 m_maxSize == attr.m_maxSize &&
12174
12175 m_border == attr.m_border &&
12176 m_outline == attr.m_outline &&
12177
12178 m_boxStyleName == attr.m_boxStyleName
12179 );
12180 }
12181
12182 // Partial equality test
12183 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
12184 {
12185 if (!weakTest &&
12186 ((!HasFloatMode() && attr.HasFloatMode()) ||
12187 (!HasClearMode() && attr.HasClearMode()) ||
12188 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12189 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12190 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12191 {
12192 return false;
12193 }
12194 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12195 return false;
12196
12197 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12198 return false;
12199
12200 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12201 return false;
12202
12203 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12204 return false;
12205
12206 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12207 return false;
12208
12209 // Position
12210
12211 if (!m_position.EqPartial(attr.m_position, weakTest))
12212 return false;
12213
12214 // Size
12215
12216 if (!m_size.EqPartial(attr.m_size, weakTest))
12217 return false;
12218 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
12219 return false;
12220 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
12221 return false;
12222
12223 // Margins
12224
12225 if (!m_margins.EqPartial(attr.m_margins, weakTest))
12226 return false;
12227
12228 // Padding
12229
12230 if (!m_padding.EqPartial(attr.m_padding, weakTest))
12231 return false;
12232
12233 // Border
12234
12235 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
12236 return false;
12237
12238 // Outline
12239
12240 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
12241 return false;
12242
12243 return true;
12244 }
12245
12246 // Merges the given attributes. If compareWith
12247 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12248 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12249 bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12250 {
12251 if (attr.HasFloatMode())
12252 {
12253 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12254 SetFloatMode(attr.GetFloatMode());
12255 }
12256
12257 if (attr.HasClearMode())
12258 {
12259 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12260 SetClearMode(attr.GetClearMode());
12261 }
12262
12263 if (attr.HasCollapseBorders())
12264 {
12265 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
12266 SetCollapseBorders(attr.GetCollapseBorders());
12267 }
12268
12269 if (attr.HasVerticalAlignment())
12270 {
12271 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12272 SetVerticalAlignment(attr.GetVerticalAlignment());
12273 }
12274
12275 if (attr.HasBoxStyleName())
12276 {
12277 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12278 SetBoxStyleName(attr.GetBoxStyleName());
12279 }
12280
12281 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12282 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12283 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
12284
12285 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
12286 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12287 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
12288
12289 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12290 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
12291
12292 return true;
12293 }
12294
12295 // Remove specified attributes from this object
12296 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12297 {
12298 if (attr.HasFloatMode())
12299 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12300
12301 if (attr.HasClearMode())
12302 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12303
12304 if (attr.HasCollapseBorders())
12305 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12306
12307 if (attr.HasVerticalAlignment())
12308 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12309
12310 if (attr.HasBoxStyleName())
12311 {
12312 SetBoxStyleName(wxEmptyString);
12313 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12314 }
12315
12316 m_margins.RemoveStyle(attr.m_margins);
12317 m_padding.RemoveStyle(attr.m_padding);
12318 m_position.RemoveStyle(attr.m_position);
12319
12320 m_size.RemoveStyle(attr.m_size);
12321 m_minSize.RemoveStyle(attr.m_minSize);
12322 m_maxSize.RemoveStyle(attr.m_maxSize);
12323
12324 m_border.RemoveStyle(attr.m_border);
12325 m_outline.RemoveStyle(attr.m_outline);
12326
12327 return true;
12328 }
12329
12330 // Collects the attributes that are common to a range of content, building up a note of
12331 // which attributes are absent in some objects and which clash in some objects.
12332 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12333 {
12334 if (attr.HasFloatMode())
12335 {
12336 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12337 {
12338 if (HasFloatMode())
12339 {
12340 if (GetFloatMode() != attr.GetFloatMode())
12341 {
12342 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12343 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12344 }
12345 }
12346 else
12347 SetFloatMode(attr.GetFloatMode());
12348 }
12349 }
12350 else
12351 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12352
12353 if (attr.HasClearMode())
12354 {
12355 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12356 {
12357 if (HasClearMode())
12358 {
12359 if (GetClearMode() != attr.GetClearMode())
12360 {
12361 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12362 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12363 }
12364 }
12365 else
12366 SetClearMode(attr.GetClearMode());
12367 }
12368 }
12369 else
12370 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12371
12372 if (attr.HasCollapseBorders())
12373 {
12374 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12375 {
12376 if (HasCollapseBorders())
12377 {
12378 if (GetCollapseBorders() != attr.GetCollapseBorders())
12379 {
12380 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12381 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12382 }
12383 }
12384 else
12385 SetCollapseBorders(attr.GetCollapseBorders());
12386 }
12387 }
12388 else
12389 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12390
12391 if (attr.HasVerticalAlignment())
12392 {
12393 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12394 {
12395 if (HasVerticalAlignment())
12396 {
12397 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12398 {
12399 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12400 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12401 }
12402 }
12403 else
12404 SetVerticalAlignment(attr.GetVerticalAlignment());
12405 }
12406 }
12407 else
12408 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12409
12410 if (attr.HasBoxStyleName())
12411 {
12412 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12413 {
12414 if (HasBoxStyleName())
12415 {
12416 if (GetBoxStyleName() != attr.GetBoxStyleName())
12417 {
12418 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12419 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12420 }
12421 }
12422 else
12423 SetBoxStyleName(attr.GetBoxStyleName());
12424 }
12425 }
12426 else
12427 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12428
12429 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12430 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12431 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12432
12433 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
12434 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12435 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
12436
12437 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12438 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12439 }
12440
12441 bool wxTextBoxAttr::IsDefault() const
12442 {
12443 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
12444 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
12445 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12446 }
12447
12448 // wxRichTextAttr
12449
12450 void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12451 {
12452 wxTextAttr::Copy(attr);
12453
12454 m_textBoxAttr = attr.m_textBoxAttr;
12455 }
12456
12457 bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12458 {
12459 if (!(wxTextAttr::operator==(attr)))
12460 return false;
12461
12462 return (m_textBoxAttr == attr.m_textBoxAttr);
12463 }
12464
12465 // Partial equality test
12466 bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
12467 {
12468 if (!(wxTextAttr::EqPartial(attr, weakTest)))
12469 return false;
12470
12471 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
12472 }
12473
12474 // Merges the given attributes. If compareWith
12475 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12476 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12477 bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12478 {
12479 wxTextAttr::Apply(style, compareWith);
12480
12481 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12482 }
12483
12484 // Remove specified attributes from this object
12485 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12486 {
12487 wxTextAttr::RemoveStyle(*this, attr);
12488
12489 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12490 }
12491
12492 // Collects the attributes that are common to a range of content, building up a note of
12493 // which attributes are absent in some objects and which clash in some objects.
12494 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12495 {
12496 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
12497
12498 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12499 }
12500
12501 // Partial equality test
12502 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
12503 {
12504 if (!weakTest &&
12505 ((!HasStyle() && border.HasStyle()) ||
12506 (!HasColour() && border.HasColour()) ||
12507 (!HasWidth() && border.HasWidth())))
12508 {
12509 return false;
12510 }
12511
12512 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
12513 return false;
12514
12515 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
12516 return false;
12517
12518 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
12519 return false;
12520
12521 return true;
12522 }
12523
12524 // Apply border to 'this', but not if the same as compareWith
12525 bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
12526 {
12527 if (border.HasStyle())
12528 {
12529 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12530 SetStyle(border.GetStyle());
12531 }
12532 if (border.HasColour())
12533 {
12534 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12535 SetColour(border.GetColourLong());
12536 }
12537 if (border.HasWidth())
12538 {
12539 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12540 SetWidth(border.GetWidth());
12541 }
12542
12543 return true;
12544 }
12545
12546 // Remove specified attributes from this object
12547 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
12548 {
12549 if (attr.HasStyle() && HasStyle())
12550 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12551 if (attr.HasColour() && HasColour())
12552 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12553 if (attr.HasWidth() && HasWidth())
12554 m_borderWidth.Reset();
12555
12556 return true;
12557 }
12558
12559 // Collects the attributes that are common to a range of content, building up a note of
12560 // which attributes are absent in some objects and which clash in some objects.
12561 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
12562 {
12563 if (attr.HasStyle())
12564 {
12565 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12566 {
12567 if (HasStyle())
12568 {
12569 if (GetStyle() != attr.GetStyle())
12570 {
12571 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12572 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12573 }
12574 }
12575 else
12576 SetStyle(attr.GetStyle());
12577 }
12578 }
12579 else
12580 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12581
12582 if (attr.HasColour())
12583 {
12584 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12585 {
12586 if (HasColour())
12587 {
12588 if (GetColour() != attr.GetColour())
12589 {
12590 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12591 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12592 }
12593 }
12594 else
12595 SetColour(attr.GetColourLong());
12596 }
12597 }
12598 else
12599 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12600
12601 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12602 }
12603
12604 // Partial equality test
12605 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
12606 {
12607 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12608 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
12609 }
12610
12611 // Apply border to 'this', but not if the same as compareWith
12612 bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
12613 {
12614 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12615 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12616 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12617 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
12618 return true;
12619 }
12620
12621 // Remove specified attributes from this object
12622 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
12623 {
12624 m_left.RemoveStyle(attr.m_left);
12625 m_right.RemoveStyle(attr.m_right);
12626 m_top.RemoveStyle(attr.m_top);
12627 m_bottom.RemoveStyle(attr.m_bottom);
12628 return true;
12629 }
12630
12631 // Collects the attributes that are common to a range of content, building up a note of
12632 // which attributes are absent in some objects and which clash in some objects.
12633 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
12634 {
12635 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12636 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12637 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12638 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12639 }
12640
12641 // Set style of all borders
12642 void wxTextAttrBorders::SetStyle(int style)
12643 {
12644 m_left.SetStyle(style);
12645 m_right.SetStyle(style);
12646 m_top.SetStyle(style);
12647 m_bottom.SetStyle(style);
12648 }
12649
12650 // Set colour of all borders
12651 void wxTextAttrBorders::SetColour(unsigned long colour)
12652 {
12653 m_left.SetColour(colour);
12654 m_right.SetColour(colour);
12655 m_top.SetColour(colour);
12656 m_bottom.SetColour(colour);
12657 }
12658
12659 void wxTextAttrBorders::SetColour(const wxColour& colour)
12660 {
12661 m_left.SetColour(colour);
12662 m_right.SetColour(colour);
12663 m_top.SetColour(colour);
12664 m_bottom.SetColour(colour);
12665 }
12666
12667 // Set width of all borders
12668 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
12669 {
12670 m_left.SetWidth(width);
12671 m_right.SetWidth(width);
12672 m_top.SetWidth(width);
12673 m_bottom.SetWidth(width);
12674 }
12675
12676 // Partial equality test
12677 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
12678 {
12679 if (!weakTest && !IsValid() && dim.IsValid())
12680 return false;
12681
12682 if (dim.IsValid() && IsValid() && !((*this) == dim))
12683 return false;
12684 else
12685 return true;
12686 }
12687
12688 bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12689 {
12690 if (dim.IsValid())
12691 {
12692 if (!(compareWith && dim == (*compareWith)))
12693 (*this) = dim;
12694 }
12695
12696 return true;
12697 }
12698
12699 // Collects the attributes that are common to a range of content, building up a note of
12700 // which attributes are absent in some objects and which clash in some objects.
12701 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12702 {
12703 if (attr.IsValid())
12704 {
12705 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
12706 {
12707 if (IsValid())
12708 {
12709 if (!((*this) == attr))
12710 {
12711 clashingAttr.SetValid(true);
12712 SetValid(false);
12713 }
12714 }
12715 else
12716 (*this) = attr;
12717 }
12718 }
12719 else
12720 absentAttr.SetValid(true);
12721 }
12722
12723 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12724 {
12725 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12726 }
12727
12728 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12729 {
12730 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12731 }
12732
12733 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12734 {
12735 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12736 }
12737
12738 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12739 {
12740 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12741 }
12742
12743 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12744 {
12745 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12746 return ConvertTenthsMMToPixels(dim.GetValue());
12747 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12748 return dim.GetValue();
12749 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12750 {
12751 wxASSERT(m_parentSize != wxDefaultSize);
12752 if (direction == wxHORIZONTAL)
12753 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12754 else
12755 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12756 }
12757 else
12758 {
12759 wxASSERT(false);
12760 return 0;
12761 }
12762 }
12763
12764 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12765 {
12766 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12767 return dim.GetValue();
12768 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12769 return ConvertPixelsToTenthsMM(dim.GetValue());
12770 else
12771 {
12772 wxASSERT(false);
12773 return 0;
12774 }
12775 }
12776
12777 // Partial equality test
12778 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
12779 {
12780 if (!m_left.EqPartial(dims.m_left, weakTest))
12781 return false;
12782
12783 if (!m_right.EqPartial(dims.m_right, weakTest))
12784 return false;
12785
12786 if (!m_top.EqPartial(dims.m_top, weakTest))
12787 return false;
12788
12789 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
12790 return false;
12791
12792 return true;
12793 }
12794
12795 // Apply border to 'this', but not if the same as compareWith
12796 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
12797 {
12798 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12799 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12800 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12801 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12802
12803 return true;
12804 }
12805
12806 // Remove specified attributes from this object
12807 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
12808 {
12809 if (attr.m_left.IsValid())
12810 m_left.Reset();
12811 if (attr.m_right.IsValid())
12812 m_right.Reset();
12813 if (attr.m_top.IsValid())
12814 m_top.Reset();
12815 if (attr.m_bottom.IsValid())
12816 m_bottom.Reset();
12817
12818 return true;
12819 }
12820
12821 // Collects the attributes that are common to a range of content, building up a note of
12822 // which attributes are absent in some objects and which clash in some objects.
12823 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
12824 {
12825 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12826 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12827 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12828 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12829 }
12830
12831 // Partial equality test
12832 bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
12833 {
12834 if (!m_width.EqPartial(size.m_width, weakTest))
12835 return false;
12836
12837 if (!m_height.EqPartial(size.m_height, weakTest))
12838 return false;
12839
12840 return true;
12841 }
12842
12843 // Apply border to 'this', but not if the same as compareWith
12844 bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12845 {
12846 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12847 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12848
12849 return true;
12850 }
12851
12852 // Remove specified attributes from this object
12853 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12854 {
12855 if (attr.m_width.IsValid())
12856 m_width.Reset();
12857 if (attr.m_height.IsValid())
12858 m_height.Reset();
12859
12860 return true;
12861 }
12862
12863 // Collects the attributes that are common to a range of content, building up a note of
12864 // which attributes are absent in some objects and which clash in some objects.
12865 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12866 {
12867 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12868 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12869 }
12870
12871 // Collects the attributes that are common to a range of content, building up a note of
12872 // which attributes are absent in some objects and which clash in some objects.
12873 void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12874 {
12875 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12876 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12877
12878 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12879
12880 // If different font size units are being used, this is a clash.
12881 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
12882 {
12883 currentStyle.SetFontSize(0);
12884 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12885 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12886 }
12887 else
12888 {
12889 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
12890 {
12891 if (currentStyle.HasFontPointSize())
12892 {
12893 if (currentStyle.GetFontSize() != attr.GetFontSize())
12894 {
12895 // Clash of attr - mark as such
12896 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12897 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12898 }
12899 }
12900 else
12901 currentStyle.SetFontSize(attr.GetFontSize());
12902 }
12903 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
12904 {
12905 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12906 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12907 }
12908
12909 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
12910 {
12911 if (currentStyle.HasFontPixelSize())
12912 {
12913 if (currentStyle.GetFontSize() != attr.GetFontSize())
12914 {
12915 // Clash of attr - mark as such
12916 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12917 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12918 }
12919 }
12920 else
12921 currentStyle.SetFontPixelSize(attr.GetFontSize());
12922 }
12923 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
12924 {
12925 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12926 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12927 }
12928 }
12929
12930 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12931 {
12932 if (currentStyle.HasFontItalic())
12933 {
12934 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
12935 {
12936 // Clash of attr - mark as such
12937 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12938 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12939 }
12940 }
12941 else
12942 currentStyle.SetFontStyle(attr.GetFontStyle());
12943 }
12944 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
12945 {
12946 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12947 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12948 }
12949
12950 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12951 {
12952 if (currentStyle.HasFontFamily())
12953 {
12954 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
12955 {
12956 // Clash of attr - mark as such
12957 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12958 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12959 }
12960 }
12961 else
12962 currentStyle.SetFontFamily(attr.GetFontFamily());
12963 }
12964 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
12965 {
12966 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12967 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12968 }
12969
12970 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12971 {
12972 if (currentStyle.HasFontWeight())
12973 {
12974 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
12975 {
12976 // Clash of attr - mark as such
12977 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12978 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12979 }
12980 }
12981 else
12982 currentStyle.SetFontWeight(attr.GetFontWeight());
12983 }
12984 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
12985 {
12986 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12987 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12988 }
12989
12990 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12991 {
12992 if (currentStyle.HasFontFaceName())
12993 {
12994 wxString faceName1(currentStyle.GetFontFaceName());
12995 wxString faceName2(attr.GetFontFaceName());
12996
12997 if (faceName1 != faceName2)
12998 {
12999 // Clash of attr - mark as such
13000 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13001 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13002 }
13003 }
13004 else
13005 currentStyle.SetFontFaceName(attr.GetFontFaceName());
13006 }
13007 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
13008 {
13009 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13010 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13011 }
13012
13013 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
13014 {
13015 if (currentStyle.HasFontUnderlined())
13016 {
13017 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
13018 {
13019 // Clash of attr - mark as such
13020 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13021 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13022 }
13023 }
13024 else
13025 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
13026 }
13027 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
13028 {
13029 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13030 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13031 }
13032
13033 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
13034 {
13035 if (currentStyle.HasFontStrikethrough())
13036 {
13037 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
13038 {
13039 // Clash of attr - mark as such
13040 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13041 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13042 }
13043 }
13044 else
13045 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13046 }
13047 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13048 {
13049 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13050 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13051 }
13052
13053 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13054 {
13055 if (currentStyle.HasTextColour())
13056 {
13057 if (currentStyle.GetTextColour() != attr.GetTextColour())
13058 {
13059 // Clash of attr - mark as such
13060 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13061 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13062 }
13063 }
13064 else
13065 currentStyle.SetTextColour(attr.GetTextColour());
13066 }
13067 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13068 {
13069 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13070 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13071 }
13072
13073 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13074 {
13075 if (currentStyle.HasBackgroundColour())
13076 {
13077 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13078 {
13079 // Clash of attr - mark as such
13080 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13081 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13082 }
13083 }
13084 else
13085 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13086 }
13087 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13088 {
13089 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13090 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13091 }
13092
13093 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13094 {
13095 if (currentStyle.HasAlignment())
13096 {
13097 if (currentStyle.GetAlignment() != attr.GetAlignment())
13098 {
13099 // Clash of attr - mark as such
13100 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13101 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13102 }
13103 }
13104 else
13105 currentStyle.SetAlignment(attr.GetAlignment());
13106 }
13107 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13108 {
13109 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13110 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13111 }
13112
13113 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13114 {
13115 if (currentStyle.HasTabs())
13116 {
13117 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13118 {
13119 // Clash of attr - mark as such
13120 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13121 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13122 }
13123 }
13124 else
13125 currentStyle.SetTabs(attr.GetTabs());
13126 }
13127 else if (!attr.HasTabs() && currentStyle.HasTabs())
13128 {
13129 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13130 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13131 }
13132
13133 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13134 {
13135 if (currentStyle.HasLeftIndent())
13136 {
13137 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13138 {
13139 // Clash of attr - mark as such
13140 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13141 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13142 }
13143 }
13144 else
13145 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13146 }
13147 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13148 {
13149 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13150 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13151 }
13152
13153 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13154 {
13155 if (currentStyle.HasRightIndent())
13156 {
13157 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13158 {
13159 // Clash of attr - mark as such
13160 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13161 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13162 }
13163 }
13164 else
13165 currentStyle.SetRightIndent(attr.GetRightIndent());
13166 }
13167 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13168 {
13169 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13170 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13171 }
13172
13173 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13174 {
13175 if (currentStyle.HasParagraphSpacingAfter())
13176 {
13177 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13178 {
13179 // Clash of attr - mark as such
13180 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13181 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13182 }
13183 }
13184 else
13185 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13186 }
13187 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13188 {
13189 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13190 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13191 }
13192
13193 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13194 {
13195 if (currentStyle.HasParagraphSpacingBefore())
13196 {
13197 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13198 {
13199 // Clash of attr - mark as such
13200 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13201 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13202 }
13203 }
13204 else
13205 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13206 }
13207 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13208 {
13209 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13210 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13211 }
13212
13213 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13214 {
13215 if (currentStyle.HasLineSpacing())
13216 {
13217 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13218 {
13219 // Clash of attr - mark as such
13220 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13221 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13222 }
13223 }
13224 else
13225 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13226 }
13227 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13228 {
13229 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13230 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13231 }
13232
13233 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13234 {
13235 if (currentStyle.HasCharacterStyleName())
13236 {
13237 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13238 {
13239 // Clash of attr - mark as such
13240 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13241 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13242 }
13243 }
13244 else
13245 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13246 }
13247 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13248 {
13249 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13250 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13251 }
13252
13253 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13254 {
13255 if (currentStyle.HasParagraphStyleName())
13256 {
13257 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13258 {
13259 // Clash of attr - mark as such
13260 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13261 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13262 }
13263 }
13264 else
13265 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13266 }
13267 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13268 {
13269 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13270 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13271 }
13272
13273 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13274 {
13275 if (currentStyle.HasListStyleName())
13276 {
13277 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13278 {
13279 // Clash of attr - mark as such
13280 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13281 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13282 }
13283 }
13284 else
13285 currentStyle.SetListStyleName(attr.GetListStyleName());
13286 }
13287 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13288 {
13289 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13290 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13291 }
13292
13293 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13294 {
13295 if (currentStyle.HasBulletStyle())
13296 {
13297 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13298 {
13299 // Clash of attr - mark as such
13300 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13301 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13302 }
13303 }
13304 else
13305 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13306 }
13307 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13308 {
13309 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13310 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13311 }
13312
13313 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13314 {
13315 if (currentStyle.HasBulletNumber())
13316 {
13317 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13318 {
13319 // Clash of attr - mark as such
13320 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13321 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13322 }
13323 }
13324 else
13325 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13326 }
13327 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13328 {
13329 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13330 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13331 }
13332
13333 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13334 {
13335 if (currentStyle.HasBulletText())
13336 {
13337 if (currentStyle.GetBulletText() != attr.GetBulletText())
13338 {
13339 // Clash of attr - mark as such
13340 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13341 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13342 }
13343 }
13344 else
13345 {
13346 currentStyle.SetBulletText(attr.GetBulletText());
13347 currentStyle.SetBulletFont(attr.GetBulletFont());
13348 }
13349 }
13350 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13351 {
13352 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13353 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13354 }
13355
13356 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13357 {
13358 if (currentStyle.HasBulletName())
13359 {
13360 if (currentStyle.GetBulletName() != attr.GetBulletName())
13361 {
13362 // Clash of attr - mark as such
13363 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13364 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13365 }
13366 }
13367 else
13368 {
13369 currentStyle.SetBulletName(attr.GetBulletName());
13370 }
13371 }
13372 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13373 {
13374 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13375 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13376 }
13377
13378 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13379 {
13380 if (currentStyle.HasURL())
13381 {
13382 if (currentStyle.GetURL() != attr.GetURL())
13383 {
13384 // Clash of attr - mark as such
13385 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13386 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13387 }
13388 }
13389 else
13390 {
13391 currentStyle.SetURL(attr.GetURL());
13392 }
13393 }
13394 else if (!attr.HasURL() && currentStyle.HasURL())
13395 {
13396 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13397 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13398 }
13399
13400 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13401 {
13402 if (currentStyle.HasTextEffects())
13403 {
13404 // We need to find the bits in the new attr that are different:
13405 // just look at those bits that are specified by the new attr.
13406
13407 // We need to remove the bits and flags that are not common between current attr
13408 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13409 // previous styles.
13410
13411 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13412 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13413
13414 if (currentRelevantTextEffects != newRelevantTextEffects)
13415 {
13416 // Find the text effects that were different, using XOR
13417 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13418
13419 // Clash of attr - mark as such
13420 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13421 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13422 }
13423 }
13424 else
13425 {
13426 currentStyle.SetTextEffects(attr.GetTextEffects());
13427 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13428 }
13429
13430 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13431 // that we've looked at so far
13432 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13433 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13434
13435 if (currentStyle.GetTextEffectFlags() == 0)
13436 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13437 }
13438 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13439 {
13440 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13441 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13442 }
13443
13444 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13445 {
13446 if (currentStyle.HasOutlineLevel())
13447 {
13448 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13449 {
13450 // Clash of attr - mark as such
13451 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13452 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13453 }
13454 }
13455 else
13456 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13457 }
13458 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13459 {
13460 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13461 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13462 }
13463 }
13464
13465 WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13466
13467 // JACS 2013-01-27
13468 WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13469
13470 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13471
13472 bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13473 {
13474 if (m_properties.GetCount() != props.GetCount())
13475 return false;
13476
13477 size_t i;
13478 for (i = 0; i < m_properties.GetCount(); i++)
13479 {
13480 const wxVariant& var1 = m_properties[i];
13481 int idx = props.Find(var1.GetName());
13482 if (idx == -1)
13483 return false;
13484 const wxVariant& var2 = props.m_properties[idx];
13485 if (!(var1 == var2))
13486 return false;
13487 }
13488
13489 return true;
13490 }
13491
13492 wxArrayString wxRichTextProperties::GetPropertyNames() const
13493 {
13494 wxArrayString arr;
13495 size_t i;
13496 for (i = 0; i < m_properties.GetCount(); i++)
13497 {
13498 arr.Add(m_properties[i].GetName());
13499 }
13500 return arr;
13501 }
13502
13503 int wxRichTextProperties::Find(const wxString& name) const
13504 {
13505 size_t i;
13506 for (i = 0; i < m_properties.GetCount(); i++)
13507 {
13508 if (m_properties[i].GetName() == name)
13509 return (int) i;
13510 }
13511 return -1;
13512 }
13513
13514 bool wxRichTextProperties::Remove(const wxString& name)
13515 {
13516 int idx = Find(name);
13517 if (idx != -1)
13518 {
13519 m_properties.RemoveAt(idx);
13520 return true;
13521 }
13522 else
13523 return false;
13524 }
13525
13526 wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13527 {
13528 int idx = Find(name);
13529 if (idx == wxNOT_FOUND)
13530 SetProperty(name, wxString());
13531 idx = Find(name);
13532 if (idx != wxNOT_FOUND)
13533 {
13534 return & (*this)[idx];
13535 }
13536 else
13537 return NULL;
13538 }
13539
13540 const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13541 {
13542 static const wxVariant nullVariant;
13543 int idx = Find(name);
13544 if (idx != -1)
13545 return m_properties[idx];
13546 else
13547 return nullVariant;
13548 }
13549
13550 wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13551 {
13552 return GetProperty(name).GetString();
13553 }
13554
13555 long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13556 {
13557 return GetProperty(name).GetLong();
13558 }
13559
13560 bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13561 {
13562 return GetProperty(name).GetBool();
13563 }
13564
13565 double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13566 {
13567 return GetProperty(name).GetDouble();
13568 }
13569
13570 void wxRichTextProperties::SetProperty(const wxVariant& variant)
13571 {
13572 wxASSERT(!variant.GetName().IsEmpty());
13573
13574 int idx = Find(variant.GetName());
13575
13576 if (idx == -1)
13577 m_properties.Add(variant);
13578 else
13579 m_properties[idx] = variant;
13580 }
13581
13582 void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13583 {
13584 int idx = Find(name);
13585 wxVariant var(variant);
13586 var.SetName(name);
13587
13588 if (idx == -1)
13589 m_properties.Add(var);
13590 else
13591 m_properties[idx] = var;
13592 }
13593
13594 void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13595 {
13596 SetProperty(name, wxVariant(value, name));
13597 }
13598
13599 void wxRichTextProperties::SetProperty(const wxString& name, long value)
13600 {
13601 SetProperty(name, wxVariant(value, name));
13602 }
13603
13604 void wxRichTextProperties::SetProperty(const wxString& name, double value)
13605 {
13606 SetProperty(name, wxVariant(value, name));
13607 }
13608
13609 void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13610 {
13611 SetProperty(name, wxVariant(value, name));
13612 }
13613
13614 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13615 {
13616 size_t i;
13617 for (i = 0; i < properties.GetCount(); i++)
13618 {
13619 wxString name = properties.GetProperties()[i].GetName();
13620 if (HasProperty(name))
13621 Remove(name);
13622 }
13623 }
13624
13625 void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13626 {
13627 size_t i;
13628 for (i = 0; i < properties.GetCount(); i++)
13629 {
13630 SetProperty(properties.GetProperties()[i]);
13631 }
13632 }
13633
13634 wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13635 {
13636 if (m_address.GetCount() == 0)
13637 return topLevelContainer;
13638
13639 wxRichTextCompositeObject* p = topLevelContainer;
13640 size_t i = 0;
13641 while (p && i < m_address.GetCount())
13642 {
13643 int pos = m_address[i];
13644 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13645 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13646 return NULL;
13647
13648 wxRichTextObject* p1 = p->GetChild(pos);
13649 if (i == (m_address.GetCount()-1))
13650 return p1;
13651
13652 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13653 i ++;
13654 }
13655 return NULL;
13656 }
13657
13658 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13659 {
13660 m_address.Clear();
13661
13662 if (topLevelContainer == obj)
13663 return true;
13664
13665 wxRichTextObject* o = obj;
13666 while (o)
13667 {
13668 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13669 if (!p)
13670 return false;
13671
13672 int pos = p->GetChildren().IndexOf(o);
13673 if (pos == -1)
13674 return false;
13675
13676 m_address.Insert(pos, 0);
13677
13678 if (p == topLevelContainer)
13679 return true;
13680
13681 o = p;
13682 }
13683 return false;
13684 }
13685
13686 // Equality test
13687 bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13688 {
13689 if (m_container != sel.m_container)
13690 return false;
13691 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13692 return false;
13693 size_t i;
13694 for (i = 0; i < m_ranges.GetCount(); i++)
13695 if (!(m_ranges[i] == sel.m_ranges[i]))
13696 return false;
13697 return true;
13698 }
13699
13700 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13701 // or none at the level of the object's container.
13702 wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13703 {
13704 if (IsValid())
13705 {
13706 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13707
13708 if (container == m_container)
13709 return m_ranges;
13710
13711 container = obj->GetContainer();
13712 while (container)
13713 {
13714 if (container->GetParent())
13715 {
13716 // If we found that our object's container is within the range of
13717 // a selection higher up, then assume the whole original object
13718 // is also selected.
13719 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13720 if (parentContainer == m_container)
13721 {
13722 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13723 {
13724 wxRichTextRangeArray ranges;
13725 ranges.Add(obj->GetRange());
13726 return ranges;
13727 }
13728 }
13729
13730 container = parentContainer;
13731 }
13732 else
13733 {
13734 container = NULL;
13735 break;
13736 }
13737 }
13738 }
13739 return wxRichTextRangeArray();
13740 }
13741
13742 // Is the given position within the selection?
13743 bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13744 {
13745 if (!IsValid())
13746 return false;
13747 else
13748 {
13749 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13750 return WithinSelection(pos, selectionRanges);
13751 }
13752 }
13753
13754 // Is the given position within the selection range?
13755 bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13756 {
13757 size_t i;
13758 for (i = 0; i < ranges.GetCount(); i++)
13759 {
13760 const wxRichTextRange& range = ranges[i];
13761 if (pos >= range.GetStart() && pos <= range.GetEnd())
13762 return true;
13763 }
13764 return false;
13765 }
13766
13767 // Is the given range completely within the selection range?
13768 bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13769 {
13770 size_t i;
13771 for (i = 0; i < ranges.GetCount(); i++)
13772 {
13773 const wxRichTextRange& eachRange = ranges[i];
13774 if (range.IsWithin(eachRange))
13775 return true;
13776 }
13777 return false;
13778 }
13779
13780 IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13781 IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13782
13783 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
13784 {
13785 Init();
13786 m_buffer = buffer;
13787 if (m_buffer && m_buffer->GetRichTextCtrl())
13788 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13789 }
13790
13791 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13792 {
13793 if (!GetVirtualAttributesEnabled())
13794 return false;
13795
13796 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13797 while (node)
13798 {
13799 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13800 if (handler->HasVirtualAttributes(obj))
13801 return true;
13802
13803 node = node->GetNext();
13804 }
13805 return false;
13806 }
13807
13808 wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13809 {
13810 wxRichTextAttr attr;
13811 if (!GetVirtualAttributesEnabled())
13812 return attr;
13813
13814 // We apply all handlers, so we can may combine several different attributes
13815 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13816 while (node)
13817 {
13818 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13819 if (handler->HasVirtualAttributes(obj))
13820 {
13821 bool success = handler->GetVirtualAttributes(attr, obj);
13822 wxASSERT(success);
13823 wxUnusedVar(success);
13824 }
13825
13826 node = node->GetNext();
13827 }
13828 return attr;
13829 }
13830
13831 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13832 {
13833 if (!GetVirtualAttributesEnabled())
13834 return false;
13835
13836 if (HasVirtualAttributes(obj))
13837 {
13838 wxRichTextAttr a(GetVirtualAttributes(obj));
13839 attr.Apply(a);
13840 return true;
13841 }
13842 else
13843 return false;
13844 }
13845
13846 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
13847 {
13848 if (!GetVirtualAttributesEnabled())
13849 return 0;
13850
13851 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13852 while (node)
13853 {
13854 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13855 int count = handler->GetVirtualSubobjectAttributesCount(obj);
13856 if (count > 0)
13857 return count;
13858
13859 node = node->GetNext();
13860 }
13861 return 0;
13862 }
13863
13864 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
13865 {
13866 if (!GetVirtualAttributesEnabled())
13867 return 0;
13868
13869 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13870 while (node)
13871 {
13872 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13873 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
13874 return positions.GetCount();
13875
13876 node = node->GetNext();
13877 }
13878 return 0;
13879 }
13880
13881 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
13882 {
13883 if (!GetVirtualAttributesEnabled())
13884 return false;
13885
13886 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13887 while (node)
13888 {
13889 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13890 if (handler->HasVirtualText(obj))
13891 return true;
13892
13893 node = node->GetNext();
13894 }
13895 return false;
13896 }
13897
13898 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
13899 {
13900 if (!GetVirtualAttributesEnabled())
13901 return false;
13902
13903 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13904 while (node)
13905 {
13906 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13907 if (handler->GetVirtualText(obj, text))
13908 return true;
13909
13910 node = node->GetNext();
13911 }
13912 return false;
13913 }
13914
13915 /// Adds a handler to the end
13916 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13917 {
13918 sm_drawingHandlers.Append(handler);
13919 }
13920
13921 /// Inserts a handler at the front
13922 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13923 {
13924 sm_drawingHandlers.Insert( handler );
13925 }
13926
13927 /// Removes a handler
13928 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13929 {
13930 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13931 if (handler)
13932 {
13933 sm_drawingHandlers.DeleteObject(handler);
13934 delete handler;
13935 return true;
13936 }
13937 else
13938 return false;
13939 }
13940
13941 wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13942 {
13943 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13944 while (node)
13945 {
13946 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13947 if (handler->GetName().Lower() == name.Lower()) return handler;
13948
13949 node = node->GetNext();
13950 }
13951 return NULL;
13952 }
13953
13954 void wxRichTextBuffer::CleanUpDrawingHandlers()
13955 {
13956 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13957 while (node)
13958 {
13959 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13960 wxList::compatibility_iterator next = node->GetNext();
13961 delete handler;
13962 node = next;
13963 }
13964
13965 sm_drawingHandlers.Clear();
13966 }
13967
13968 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13969 {
13970 sm_fieldTypes[fieldType->GetName()] = fieldType;
13971 }
13972
13973 bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13974 {
13975 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13976 if (it == sm_fieldTypes.end())
13977 return false;
13978 else
13979 {
13980 wxRichTextFieldType* fieldType = it->second;
13981 sm_fieldTypes.erase(it);
13982 delete fieldType;
13983 return true;
13984 }
13985 }
13986
13987 wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13988 {
13989 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13990 if (it == sm_fieldTypes.end())
13991 return NULL;
13992 else
13993 return it->second;
13994 }
13995
13996 void wxRichTextBuffer::CleanUpFieldTypes()
13997 {
13998 wxRichTextFieldTypeHashMap::iterator it;
13999 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
14000 {
14001 wxRichTextFieldType* fieldType = it->second;
14002 delete fieldType;
14003 }
14004
14005 sm_fieldTypes.clear();
14006 }
14007
14008 #endif
14009 // wxUSE_RICHTEXT